xmlread.pp 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576
  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. EXMLReadError = class(Exception);
  21. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  22. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text); overload;
  23. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: TStream); overload;
  24. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: TStream; const ABaseURI: String); overload;
  25. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String); overload;
  26. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text); overload;
  27. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream); overload;
  28. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream; const ABaseURI: String); overload;
  29. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  30. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text); overload;
  31. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream); overload;
  32. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream; const ABaseURI: String); overload;
  33. // =======================================================
  34. implementation
  35. uses
  36. UriParser;
  37. type
  38. TSetOfChar = set of Char;
  39. const
  40. Letter = ['A'..'Z', 'a'..'z'];
  41. Digit = ['0'..'9'];
  42. PubidChars: TSetOfChar = [' ', #13, #10, 'a'..'z', 'A'..'Z', '0'..'9',
  43. '-', '''', '(', ')', '+', ',', '.', '/', ':', '=', '?', ';', '!', '*',
  44. '#', '@', '$', '_', '%'];
  45. type
  46. TDOMNotationEx = class(TDOMNotation);
  47. TDOMAttrEx = class(TDOMAttr);
  48. TXMLInputSource = class;
  49. TDOMElementDef = class;
  50. TDOMEntityEx = class(TDOMEntity)
  51. protected
  52. FInternal: Boolean;
  53. FResolved: Boolean;
  54. FOnStack: Boolean;
  55. FReplacementText: DOMString;
  56. end;
  57. // TODO: Do I need PEMap in DocType? Maybe move it to Reader itself?
  58. // (memory usage - they are not needed after parsing)
  59. TDOMDocumentTypeEx = class(TDOMDocumentType)
  60. private
  61. FHasPERefs: Boolean;
  62. FPEMap: TDOMNamedNodeMap;
  63. FElementDefs: TDOMNamedNodeMap;
  64. function GetPEMap: TDOMNamedNodeMap;
  65. function GetElementDefs: TDOMNamedNodeMap;
  66. protected
  67. property PEMap: TDOMNamedNodeMap read GetPEMap;
  68. property ElementDefs: TDOMNamedNodeMap read GetElementDefs;
  69. property HasPERefs: Boolean read FHasPERefs write FHasPERefs;
  70. public
  71. destructor Destroy; override;
  72. end;
  73. TXMLReader = class;
  74. TDecoder = class;
  75. TDecoderRef = class of TDecoder;
  76. TXMLInputSource = class
  77. private
  78. FBuf: PChar;
  79. FBufEnd: PChar;
  80. FEof: Boolean;
  81. FSurrogate: WideChar;
  82. FReader: TXMLReader;
  83. FParent: TXMLInputSource;
  84. FEntity: TObject; // weak reference
  85. FCursor: TObject; // weak reference
  86. FLine: Integer;
  87. FColumn: Integer;
  88. FSystemID: WideString;
  89. FPublicID: WideString;
  90. function GetSystemID: WideString;
  91. function GetPublicID: WideString;
  92. protected
  93. procedure FetchData; virtual;
  94. public
  95. constructor Create(const AData: WideString);
  96. function NextChar: WideChar; virtual;
  97. procedure Initialize; virtual;
  98. procedure SetEncoding(const AEncoding: string); virtual;
  99. property SystemID: WideString read GetSystemID write FSystemID;
  100. property PublicID: WideString read GetPublicID write FPublicID;
  101. end;
  102. TXMLDecodingSource = class(TXMLInputSource)
  103. private
  104. FDecoder: TDecoder;
  105. FSeenCR: Boolean;
  106. function InternalNextChar: WideChar;
  107. procedure DecodingError(const Msg: string); overload;
  108. procedure DecodingError(const Msg: string; const Args: array of const); overload;
  109. public
  110. destructor Destroy; override;
  111. function NextChar: WideChar; override;
  112. procedure SetEncoding(const AEncoding: string); override;
  113. procedure Initialize; override;
  114. end;
  115. TXMLStreamInputSource = class(TXMLDecodingSource)
  116. private
  117. FAllocated: PChar;
  118. FStream: TStream;
  119. FBufSize: Integer;
  120. FOwnStream: Boolean;
  121. public
  122. constructor Create(AStream: TStream; AOwnStream: Boolean);
  123. destructor Destroy; override;
  124. procedure FetchData; override;
  125. end;
  126. TXMLFileInputSource = class(TXMLDecodingSource)
  127. private
  128. FFile: ^Text;
  129. FString: string;
  130. public
  131. constructor Create(var AFile: Text);
  132. procedure FetchData; override;
  133. end;
  134. TDecoder = class
  135. private
  136. FSource: TXMLDecodingSource;
  137. public
  138. constructor Create(ASource: TXMLDecodingSource);
  139. function DecodeNext: WideChar; virtual; abstract;
  140. class function Supports(const AEncoding: string): Boolean; virtual; abstract;
  141. end;
  142. TISO8859_1Decoder = class(TDecoder)
  143. public
  144. function DecodeNext: WideChar; override;
  145. class function Supports(const AEncoding: string): Boolean; override;
  146. end;
  147. TUCS2Decoder = class(TDecoder)
  148. private
  149. FSwapEndian: Boolean;
  150. FEncoding: string;
  151. public
  152. function DecodeNext: WideChar; override;
  153. class function Supports(const AEncoding: string): Boolean; override;
  154. end;
  155. TUTF8Decoder = class(TDecoder)
  156. public
  157. function DecodeNext: WideChar; override;
  158. class function Supports(const AEncoding: string): Boolean; override;
  159. end;
  160. PWideCharBuf = ^TWideCharBuf;
  161. TWideCharBuf = record
  162. Buffer: PWideChar;
  163. Length: Integer;
  164. MaxLength: Integer;
  165. end;
  166. TEntityResolveEvent = procedure(const PubID, SysID: WideString; var Source: TXMLInputSource) of object;
  167. TDeclType = (dtNone, dtXml, dtText);
  168. TXMLReader = class
  169. private
  170. FSource: TXMLInputSource;
  171. FCurChar: WideChar;
  172. FWhitespace: Boolean;
  173. FXML11: Boolean;
  174. FValue: TWideCharBuf;
  175. FName: TWideCharBuf;
  176. FCopyBuf: PWideCharBuf;
  177. FIntSubset: Boolean;
  178. FAllowedDecl: TDeclType;
  179. FDtdParsed: Boolean;
  180. FRecognizePE: Boolean;
  181. FStandalone: Boolean; // property of Doc ?
  182. FInvalid: Boolean;
  183. // TODO: This array must be stored globally, not per instance
  184. FNamePages: PByteArray;
  185. FForbiddenAscii: TSetOfChar;
  186. FDocType: TDOMDocumentTypeEx; // a shortcut
  187. FEntityLevel: Integer;
  188. FPreserveWhitespace: Boolean;
  189. FCreateEntityRefs: Boolean;
  190. procedure RaiseExpectedQmark;
  191. procedure GetChar;
  192. procedure GetCharRaw;
  193. procedure Unget(wc: WideChar);
  194. procedure Initialize(ASource: TXMLInputSource);
  195. procedure InitializeRoot(ASource: TXMLInputSource);
  196. procedure DoParseAttValue(Delim: WideChar);
  197. procedure DoParseFragment;
  198. procedure DoParseExtSubset(ASource: TXMLInputSource);
  199. function ContextPush(AEntity: TDOMEntityEx): Boolean;
  200. function ContextPop: Boolean;
  201. procedure XML11_BuildTables;
  202. function XML11_CheckName: Boolean;
  203. protected
  204. FCursor: TDOMNode;
  205. procedure RaiseExc(const descr: String); overload;
  206. procedure RaiseExc(const descr: string; const args: array of const); overload;
  207. procedure RaiseExc(Expected: WideChar); overload;
  208. function SkipWhitespace: Boolean;
  209. procedure ExpectWhitespace;
  210. procedure ExpectString(const s: String);
  211. procedure ExpectChar(wc: WideChar);
  212. function CheckForChar(c: WideChar): Boolean;
  213. procedure SkipString(const ValidChars: TSetOfChar);
  214. function GetString(const ValidChars: TSetOfChar): WideString;
  215. procedure RaiseNameNotFound;
  216. function CheckName: Boolean;
  217. function CheckNmToken: Boolean;
  218. function ExpectName: WideString; // [5]
  219. procedure SkipName;
  220. function SkipQuotedLiteral: Boolean;
  221. procedure ExpectAttValue; // [10]
  222. procedure SkipPubidLiteral; // [12]
  223. procedure SkipSystemLiteral(out Literal: WideString; Required: Boolean);
  224. procedure ParseComment; // [15]
  225. procedure ParsePI; // [16]
  226. procedure ParseCDSect; // [18]
  227. procedure ParseXmlOrTextDecl(TextDecl: Boolean);
  228. function ParseEq: Boolean; // [25]
  229. procedure ExpectEq;
  230. procedure ParseMisc; // [27]
  231. procedure ParseDoctypeDecl; // [28]
  232. procedure ParseMarkupDecl; // [29]
  233. procedure ParseElement; // [39]
  234. procedure ParseContent; // [43]
  235. function ResolvePredefined(const RefName: WideString): WideChar;
  236. procedure IncludeEntity(AEntity: TDOMEntityEx; InAttr: Boolean);
  237. procedure StartPE;
  238. function ParseCharRef: Boolean; // [66]
  239. function ParseReference: TDOMEntityEx; // [67]
  240. function ParsePEReference: Boolean; // [69]
  241. function ParseExternalID(out SysID, PubID: WideString; // [75]
  242. SysIdOptional: Boolean): Boolean;
  243. procedure ProcessTextAndRefs;
  244. procedure AssertPENesting(CurrentLevel: Integer);
  245. procedure ParseEntityDecl;
  246. procedure ParseEntityDeclValue(Delim: WideChar);
  247. procedure ParseAttlistDecl;
  248. procedure ExpectChoiceOrSeq;
  249. procedure ParseMixedOrChildren;
  250. procedure ParseElementDecl;
  251. procedure ParseNotationDecl;
  252. function ResolveEntity(const SystemID, PublicID: WideString; out Source: TXMLInputSource): Boolean;
  253. procedure ProcessDefaultAttributes(Element: TDOMElement);
  254. procedure ValidationError(const Msg: string; const args: array of const);
  255. public
  256. doc: TDOMDocument;
  257. constructor Create;
  258. destructor Destroy; override;
  259. procedure ProcessXML(ASource: TXMLInputSource); // [1]
  260. procedure ProcessFragment(ASource: TXMLInputSource; AOwner: TDOMNode);
  261. procedure ProcessDTD(ASource: TXMLInputSource); // ([29])
  262. end;
  263. // AttDef/ElementDef support
  264. TAttrDataType = (
  265. DT_CDATA,
  266. DT_ID,
  267. DT_IDREF,
  268. DT_IDREFS,
  269. DT_ENTITY,
  270. DT_ENTITIES,
  271. DT_NMTOKEN,
  272. DT_NMTOKENS,
  273. DT_NOTATION
  274. );
  275. TAttrDefault = (
  276. AD_IMPLIED,
  277. AD_DEFAULT,
  278. AD_REQUIRED,
  279. AD_FIXED
  280. );
  281. TDOMAttrDef = class(TDOMAttr)
  282. protected
  283. FDataType: TAttrDataType;
  284. FDefault: TAttrDefault;
  285. // FEnumeration: TWideStringList? array of WideStrings?
  286. end;
  287. TDOMElementDef = class(TDOMElement);
  288. {$i names.inc}
  289. // TODO: List of registered/supported decoders
  290. function FindDecoder(const Encoding: string): TDecoderRef;
  291. begin
  292. if TISO8859_1Decoder.Supports(Encoding) then
  293. Result := TISO8859_1Decoder
  294. else
  295. Result := nil;
  296. end;
  297. procedure BufAllocate(var ABuffer: TWideCharBuf; ALength: Integer);
  298. begin
  299. ABuffer.MaxLength := ALength;
  300. ABuffer.Length := 0;
  301. GetMem(ABuffer.Buffer, ABuffer.MaxLength*SizeOf(WideChar));
  302. end;
  303. procedure BufAppend(var ABuffer: TWideCharBuf; wc: WideChar);
  304. begin
  305. if ABuffer.Length >= ABuffer.MaxLength then
  306. begin
  307. ABuffer.MaxLength := ABuffer.MaxLength * 2;
  308. ReallocMem(ABuffer.Buffer, ABuffer.MaxLength * SizeOf(WideChar));
  309. end;
  310. ABuffer.Buffer[ABuffer.Length] := wc;
  311. Inc(ABuffer.Length);
  312. end;
  313. function IsValidEncName(const s: WideString): Boolean;
  314. var
  315. I: Integer;
  316. begin
  317. Result := False;
  318. if (s = '') or (s[1] > #255) or not (char(s[1]) in ['A'..'Z', 'a'..'z']) then
  319. Exit;
  320. for I := 2 to Length(s) do
  321. if (s[I] > #255) or not (char(s[I]) in ['A'..'Z', 'a'..'z', '0'..'9', '.', '_', '-']) then
  322. Exit;
  323. Result := True;
  324. end;
  325. { TDOMDocumentTypeEx }
  326. destructor TDOMDocumentTypeEx.Destroy;
  327. begin
  328. FPEMap.Free;
  329. FElementDefs.Free;
  330. inherited Destroy;
  331. end;
  332. function TDOMDocumentTypeEx.GetElementDefs: TDOMNamedNodeMap;
  333. begin
  334. if FElementDefs = nil then
  335. FElementDefs := TDOMNamedNodeMap.Create(Self, ELEMENT_NODE);
  336. Result := FElementDefs;
  337. end;
  338. function TDOMDocumentTypeEx.GetPEMap: TDOMNamedNodeMap;
  339. begin
  340. if FPEMap = nil then
  341. FPEMap := TDOMNamedNodeMap.Create(Self, ENTITY_NODE);
  342. Result := FPEMap;
  343. end;
  344. // TODO: These classes still cannot be considered as the final solution...
  345. { TXMLInputSource }
  346. constructor TXMLInputSource.Create(const AData: WideString);
  347. begin
  348. inherited Create;
  349. FBuf := PChar(PWideChar(AData));
  350. FBufEnd := FBuf + Length(AData) * sizeof(WideChar);
  351. end;
  352. procedure TXMLInputSource.Initialize;
  353. begin
  354. FLine := 1;
  355. FColumn := 0;
  356. end;
  357. function TXMLInputSource.NextChar: WideChar;
  358. begin
  359. if FSurrogate <> #0 then
  360. begin
  361. Result := FSurrogate;
  362. FSurrogate := #0;
  363. end
  364. else if FBufEnd <= FBuf then
  365. begin
  366. Result := #0;
  367. FEof := True;
  368. end
  369. else
  370. begin
  371. Result := PWideChar(FBuf)^;
  372. Inc(FBuf, sizeof(WideChar));
  373. end;
  374. // TODO: Column counting - surrogate pair is a single char!
  375. if Result = #10 then
  376. begin
  377. Inc(FLine);
  378. FColumn := 0;
  379. end
  380. else
  381. Inc(FColumn);
  382. end;
  383. procedure TXMLDecodingSource.DecodingError(const Msg: string);
  384. begin
  385. FReader.RaiseExc(Msg);
  386. end;
  387. procedure TXMLDecodingSource.DecodingError(const Msg: string;
  388. const Args: array of const);
  389. begin
  390. FReader.RaiseExc(Msg, Args);
  391. end;
  392. procedure TXMLInputSource.FetchData;
  393. begin
  394. FEof := True;
  395. end;
  396. procedure TXMLInputSource.SetEncoding(const AEncoding: string);
  397. begin
  398. // do nothing
  399. end;
  400. function TXMLInputSource.GetPublicID: WideString;
  401. begin
  402. if FPublicID <> '' then
  403. Result := FPublicID
  404. else if Assigned(FParent) then
  405. Result := FParent.PublicID
  406. else
  407. Result := '';
  408. end;
  409. function TXMLInputSource.GetSystemID: WideString;
  410. begin
  411. if FSystemID <> '' then
  412. Result := FSystemID
  413. else if Assigned(FParent) then
  414. Result := FParent.SystemID
  415. else
  416. Result := '';
  417. end;
  418. { TXMLDecodingSource }
  419. destructor TXMLDecodingSource.Destroy;
  420. begin
  421. FDecoder.Free;
  422. inherited Destroy;
  423. end;
  424. function TXMLDecodingSource.InternalNextChar: WideChar;
  425. begin
  426. // TODO: find a place for it, finally...
  427. if FSurrogate <> #0 then
  428. begin
  429. Result := FSurrogate;
  430. FSurrogate := #0;
  431. Exit;
  432. end;
  433. if FBufEnd <= FBuf then
  434. FetchData;
  435. if not FEof then
  436. Result := FDecoder.DecodeNext
  437. else
  438. Result := #0;
  439. end;
  440. function TXMLDecodingSource.NextChar: WideChar;
  441. begin
  442. Result := InternalNextChar;
  443. if FSeenCR then
  444. begin
  445. if (Result = #10) or ((Result = #$85) and FReader.FXML11) then
  446. Result := InternalNextChar;
  447. FSeenCR := False;
  448. end;
  449. case Result of
  450. #13: begin
  451. FSeenCR := True;
  452. Result := #10;
  453. end;
  454. #$85, #$2028:
  455. if FReader.FXML11 then
  456. Result := #10;
  457. end;
  458. if (Result < #256) and (char(Result) in FReader.FForbiddenAscii) or
  459. ((ord(Result) or 1) = $FFFF) then
  460. DecodingError('Invalid character');
  461. // TODO: Column counting - surrogate pair is a single char!
  462. if Result = #10 then
  463. begin
  464. Inc(FLine);
  465. FColumn := 0;
  466. end
  467. else
  468. Inc(FColumn);
  469. end;
  470. procedure TXMLDecodingSource.Initialize;
  471. begin
  472. inherited;
  473. if FBufEnd-FBuf > 1 then
  474. repeat
  475. if (FBuf[0] = #$FE) and (FBuf[1] = #$FF) then // BE
  476. begin
  477. FDecoder := TUCS2Decoder.Create(Self);
  478. TUCS2Decoder(FDecoder).FEncoding := 'UTF-16BE';
  479. {$IFNDEF ENDIAN_BIG}
  480. TUCS2Decoder(FDecoder).FSwapEndian := True;
  481. {$ENDIF}
  482. Exit;
  483. end
  484. else if (FBuf[0] = #$FF) and (FBuf[1] = #$FE) then // LE
  485. begin
  486. FDecoder := TUCS2Decoder.Create(Self);
  487. TUCS2Decoder(FDecoder).FEncoding := 'UTF-16LE';
  488. {$IFDEF ENDIAN_BIG}
  489. TUCS2Decoder(FDecoder).FSwapEndian := True;
  490. {$ENDIF}
  491. Exit;
  492. end
  493. else
  494. Break;
  495. until False;
  496. FDecoder := TUTF8Decoder.Create(Self);
  497. end;
  498. procedure TXMLDecodingSource.SetEncoding(const AEncoding: string);
  499. var
  500. NewDecoder: TDecoderRef;
  501. begin
  502. if FDecoder.Supports(AEncoding) then // no change needed
  503. Exit;
  504. // hardcoded stuff - special case of UCS2
  505. if FDecoder is TUCS2Decoder then
  506. begin
  507. // check for 'UTF-16LE' or 'UTF-16BE'
  508. if SameText(AEncoding, TUCS2Decoder(FDecoder).FEncoding) then
  509. Exit
  510. else
  511. DecodingError('Current encoding cannot be switched to ''%s''', [AEncoding]);
  512. end;
  513. NewDecoder := FindDecoder(AEncoding);
  514. if Assigned(NewDecoder) then
  515. begin
  516. FDecoder.Free;
  517. FDecoder := NewDecoder.Create(Self);
  518. end
  519. else
  520. DecodingError('Encoding ''%s'' is not supported', [AEncoding]);
  521. end;
  522. { TXMLStreamInputSource }
  523. constructor TXMLStreamInputSource.Create(AStream: TStream; AOwnStream: Boolean);
  524. begin
  525. FStream := AStream;
  526. FBufSize := 4096;
  527. GetMem(FAllocated, FBufSize+8);
  528. FBuf := FAllocated+8;
  529. FBufEnd := FBuf;
  530. FOwnStream := AOwnStream;
  531. FetchData;
  532. end;
  533. destructor TXMLStreamInputSource.Destroy;
  534. begin
  535. FreeMem(FAllocated);
  536. if FOwnStream then
  537. FStream.Free;
  538. inherited Destroy;
  539. end;
  540. procedure TXMLStreamInputSource.FetchData;
  541. var
  542. Remainder, BytesRead: Integer;
  543. OldBuf: PChar;
  544. begin
  545. Assert(FBufEnd - FBuf < 8);
  546. OldBuf := FBuf;
  547. Remainder := FBufEnd - FBuf;
  548. FBuf := FAllocated+8-Remainder;
  549. Move(OldBuf^, FBuf^, Remainder);
  550. BytesRead := FStream.Read(FAllocated[8], FBufSize);
  551. if BytesRead = 0 then
  552. FEof := True;
  553. FBufEnd := FAllocated + 8 + BytesRead;
  554. end;
  555. { TXMLFileInputSource }
  556. constructor TXMLFileInputSource.Create(var AFile: Text);
  557. begin
  558. FFile := @AFile;
  559. ReadLn(FFile^, FString);
  560. FBuf := PChar(FString);
  561. FBufEnd := FBuf + Length(FString);
  562. end;
  563. procedure TXMLFileInputSource.FetchData;
  564. begin
  565. FEof := Eof(FFile^);
  566. if not FEof then
  567. begin
  568. ReadLn(FFile^, FString);
  569. FString := FString + #10; // bad solution...
  570. FBuf := PChar(FString);
  571. FBufEnd := FBuf + Length(FString);
  572. end;
  573. end;
  574. { TDecoder }
  575. constructor TDecoder.Create(ASource: TXMLDecodingSource);
  576. begin
  577. inherited Create;
  578. FSource := ASource;
  579. end;
  580. { TISO8859_1Decoder}
  581. function TISO8859_1Decoder.DecodeNext: WideChar;
  582. begin
  583. with FSource do
  584. begin
  585. Result := WideChar(FBuf[0]);
  586. Inc(FBuf);
  587. end;
  588. end;
  589. class function TISO8859_1Decoder.Supports(const AEncoding: string): Boolean;
  590. begin
  591. Result := SameText(AEncoding, 'ISO-8859-1') or
  592. SameText(AEncoding, 'ISO_8859-1') or
  593. SameText(AEncoding, 'latin1') or
  594. SameText(AEncoding, 'iso-ir-100') or
  595. SameText(AEncoding, 'l1') or
  596. SameText(AEncoding, 'IBM819') or
  597. SameText(AEncoding, 'CP819') or
  598. SameText(AEncoding, 'csISOLatin1') or
  599. // This one is not in character-sets.txt, but used in most FPC documentation...
  600. SameText(AEncoding, 'ISO8859-1');
  601. end;
  602. { TUCS2Decoder }
  603. function TUCS2Decoder.DecodeNext: WideChar;
  604. begin
  605. with FSource do
  606. begin
  607. Result := PWideChar(FBuf)^;
  608. Inc(FBuf, sizeof(WideChar));
  609. end;
  610. if FSwapEndian then
  611. Result := WideChar(Swap(Word(Result)));
  612. end;
  613. class function TUCS2Decoder.Supports(const AEncoding: string): Boolean;
  614. begin
  615. // generic aliases for both LE and BE
  616. Result := SameText(AEncoding, 'UTF-16') or
  617. SameText(AEncoding, 'unicode');
  618. end;
  619. { TUTF8Decoder }
  620. function TUTF8Decoder.DecodeNext: WideChar;
  621. const
  622. MaxCode: array[0..3] of Cardinal = ($7F, $7FF, $FFFF, $1FFFFF);
  623. var
  624. Value: Cardinal;
  625. I, bc: Integer;
  626. begin
  627. with FSource do
  628. begin
  629. Result := WideChar(FBuf[0]);
  630. Inc(FBuf);
  631. if Result < #$80 then
  632. Exit;
  633. if Byte(Result) and $40 = 0 then
  634. DecodingError('Invalid UTF8 sequence start byte');
  635. bc := 1;
  636. if Byte(Result) and $20 <> 0 then
  637. begin
  638. Inc(bc);
  639. if Byte(Result) and $10 <> 0 then
  640. begin
  641. Inc(bc);
  642. if Byte(Result) and $8 <> 0 then
  643. DecodingError('UCS4 character out of supported range');
  644. end;
  645. end;
  646. // DONE: (?) check that bc bytes available
  647. if FBufEnd-FBuf < bc then
  648. FetchData;
  649. Value := Byte(Result);
  650. I := bc; // note: I is never zero
  651. while bc > 0 do
  652. begin
  653. if ord(FBuf[0]) and $C0 <> $80 then
  654. DecodingError('Invalid byte in UTF8 sequence');
  655. Value := (Value shl 6) or (Cardinal(FBuf[0]) and $3F);
  656. Inc(FBuf);
  657. Dec(bc);
  658. end;
  659. Value := Value and MaxCode[I];
  660. // RFC2279 check
  661. if Value <= MaxCode[I-1] then
  662. DecodingError('Invalid UTF8 sequence');
  663. case Value of
  664. 0..$D7FF, $E000..$FFFF:
  665. begin
  666. Result := WideChar(Value);
  667. Exit;
  668. end;
  669. $10000..$10FFFF:
  670. begin
  671. Result := WideChar($D7C0 + (Value shr 10));
  672. FSurrogate := WideChar($DC00 xor (Value and $3FF));
  673. Exit;
  674. end;
  675. end;
  676. DecodingError('UCS4 character out of supported range');
  677. end;
  678. end;
  679. class function TUTF8Decoder.Supports(const AEncoding: string): Boolean;
  680. begin
  681. Result := SameText(AEncoding, 'UTF-8');
  682. end;
  683. { TXMLReader }
  684. function TXMLReader.ResolveEntity(const SystemID, PublicID: WideString; out Source: TXMLInputSource): Boolean;
  685. var
  686. AbsSysID: WideString;
  687. Filename: string;
  688. Stream: TStream;
  689. begin
  690. Result := False;
  691. if ResolveRelativeURI(FSource.SystemID, SystemID, AbsSysID) then
  692. begin
  693. Source := nil;
  694. // TODO: alternative resolvers
  695. if URIToFilename(AbsSysID, Filename) then
  696. begin
  697. try
  698. Stream := TFileStream.Create(Filename, fmOpenRead + fmShareDenyWrite);
  699. Source := TXMLStreamInputSource.Create(Stream, True);
  700. Source.SystemID := AbsSysID;
  701. Source.PublicID := PublicID;
  702. Result := True;
  703. except
  704. on E: Exception do
  705. ValidationError('%s', [E.Message]);
  706. end;
  707. end;
  708. end;
  709. end;
  710. procedure TXMLReader.InitializeRoot(ASource: TXMLInputSource);
  711. begin
  712. Initialize(ASource);
  713. GetChar;
  714. // TODO: presence of BOM must prevent UTF-8 encoding from being changed
  715. CheckForChar(#$FEFF); // skip BOM, if one is present
  716. end;
  717. procedure TXMLReader.Initialize(ASource: TXMLInputSource);
  718. begin
  719. FSource := ASource;
  720. FSource.FReader := Self;
  721. FSource.Initialize;
  722. end;
  723. procedure TXMLReader.GetCharRaw;
  724. begin
  725. FCurChar := FSource.NextChar;
  726. FWhitespace := (FCurChar = #32) or (FCurChar = #10) or
  727. (FCurChar = #9) or (FCurChar = #13);
  728. // Used for handling the internal DTD subset
  729. if Assigned(FCopyBuf) and (FSource.FParent = nil) then
  730. BufAppend(FCopyBuf^, FCurChar);
  731. end;
  732. procedure TXMLReader.GetChar;
  733. begin
  734. GetCharRaw;
  735. if not FRecognizePE then
  736. Exit;
  737. if (FCurChar = #0) and ContextPop then
  738. begin
  739. Unget(FCurChar);
  740. FCurChar := #32;
  741. FWhitespace := True;
  742. end
  743. else if FCurChar = '%' then
  744. begin
  745. FCurChar := FSource.NextChar;
  746. if not CheckName then
  747. begin
  748. Unget(FCurChar);
  749. FCurChar := '%';
  750. Exit;
  751. end;
  752. if FCurChar = ';' then // "%pe1;%pe2" - must not recognize pe2 immediately!
  753. GetCharRaw
  754. else
  755. RaiseExc(WideChar(';'));
  756. StartPE;
  757. FCurChar := #32;
  758. FWhitespace := True;
  759. end;
  760. end;
  761. procedure TXMLReader.Unget(wc: WideChar);
  762. begin
  763. FSource.FSurrogate := wc;
  764. end;
  765. procedure TXMLReader.RaiseExpectedQmark;
  766. begin
  767. RaiseExc('Expected single or double quote');
  768. end;
  769. procedure TXMLReader.RaiseExc(Expected: WideChar);
  770. begin
  771. // FIX: don't output what is found - anything may be found, including exploits...
  772. RaiseExc('Expected "%1s"', [string(Expected)]);
  773. end;
  774. procedure TXMLReader.RaiseExc(const descr: String);
  775. begin
  776. raise EXMLReadError.CreateFmt('In ''%s'' (line %d pos %d): %s', [FSource.SystemID, FSource.FLine, FSource.FColumn, descr]);
  777. end;
  778. procedure TXMLReader.RaiseExc(const descr: string; const args: array of const);
  779. begin
  780. RaiseExc(Format(descr, args));
  781. end;
  782. function TXMLReader.SkipWhitespace: Boolean;
  783. begin
  784. Result := False;
  785. while FWhitespace do
  786. begin
  787. GetChar;
  788. Result := True;
  789. end;
  790. end;
  791. procedure TXMLReader.ExpectWhitespace;
  792. begin
  793. if not SkipWhitespace then
  794. RaiseExc('Expected whitespace');
  795. end;
  796. procedure TXMLReader.ExpectChar(wc: WideChar);
  797. begin
  798. if FCurChar = wc then
  799. GetChar
  800. else
  801. RaiseExc(wc);
  802. end;
  803. procedure TXMLReader.ExpectString(const s: String);
  804. var
  805. I: Integer;
  806. begin
  807. for I := 1 to Length(s) do
  808. begin
  809. if FCurChar <> WideChar(s[i]) then
  810. RaiseExc('Expected "%s"', [s]);
  811. GetChar;
  812. end;
  813. end;
  814. function TXMLReader.CheckForChar(c: WideChar): Boolean;
  815. begin
  816. Result := (FCurChar = c);
  817. if Result then
  818. GetChar;
  819. end;
  820. procedure TXMLReader.SkipString(const ValidChars: TSetOfChar);
  821. begin
  822. FValue.Length := 0;
  823. while (ord(FCurChar) < 256) and (char(FCurChar) in ValidChars) do
  824. begin
  825. BufAppend(FValue, FCurChar);
  826. GetChar;
  827. end;
  828. end;
  829. function TXMLReader.GetString(const ValidChars: TSetOfChar): WideString;
  830. begin
  831. SkipString(ValidChars);
  832. SetString(Result, FValue.Buffer, FValue.Length);
  833. end;
  834. constructor TXMLReader.Create;
  835. begin
  836. inherited Create;
  837. // Naming bitmap: Point to static data for XML 1.0,
  838. // and allocate buffer in XML11_BuildTables when necessary.
  839. FNamePages := @NamePages;
  840. BufAllocate(FName, 128);
  841. BufAllocate(FValue, 512);
  842. FForbiddenAscii := [#1..#8, #11..#12, #14..#31];
  843. // TODO: put under user control
  844. FPreserveWhitespace := True;
  845. FCreateEntityRefs := True;
  846. end;
  847. destructor TXMLReader.Destroy;
  848. begin
  849. if FXML11 then
  850. FreeMem(FNamePages);
  851. FreeMem(FName.Buffer);
  852. FreeMem(FValue.Buffer);
  853. while ContextPop do; // clean input stack
  854. FSource.Free;
  855. inherited Destroy;
  856. end;
  857. procedure TXMLReader.XML11_BuildTables;
  858. var
  859. I: Integer;
  860. begin
  861. if not FXML11 then
  862. GetMem(FNamePages, 512);
  863. FXML11 := True;
  864. for I := 0 to 255 do
  865. FNamePages^[I] := ord(Byte(I) in Xml11HighPages);
  866. FNamePages^[0] := 2;
  867. FNamePages^[3] := $2c;
  868. FNamePages^[$20] := $2a;
  869. FNamePages^[$21] := $2b;
  870. FNamePages^[$2f] := $29;
  871. FNamePages^[$30] := $2d;
  872. FNamePages^[$fd] := $28;
  873. Move(FNamePages^, FNamePages^[256], 256);
  874. FNamePages^[$100] := $19;
  875. FNamePages^[$103] := $2E;
  876. FNamePages^[$120] := $2F;
  877. FForbiddenAscii := [#1..#8, #11..#12, #14..#31, #$7F..#$84, #$86..#$9F];
  878. end;
  879. procedure TXMLReader.ProcessXML(ASource: TXMLInputSource);
  880. begin
  881. doc := TXMLDocument.Create;
  882. FCursor := doc;
  883. InitializeRoot(ASource);
  884. FAllowedDecl := dtXml;
  885. ParseMisc;
  886. FDtdParsed := True;
  887. if FDocType = nil then
  888. ValidationError('Missing DTD', []);
  889. if CheckName then
  890. ParseElement
  891. else
  892. RaiseExc('Expected element');
  893. ParseMisc;
  894. if Assigned(FDocType) and (doc.DocumentElement.TagName <> FDocType.Name) then
  895. ValidationError('DTD name does not match root element', []);
  896. if FCurChar <> #0 then
  897. RaiseExc('Text after end of document element found');
  898. end;
  899. procedure TXMLReader.ProcessFragment(ASource: TXMLInputSource; AOwner: TDOMNode);
  900. begin
  901. doc := AOwner.OwnerDocument;
  902. FCursor := AOwner;
  903. InitializeRoot(ASource);
  904. FXML11 := doc.InheritsFrom(TXMLDocument) and (TXMLDocument(doc).XMLVersion = '1.1');
  905. FAllowedDecl := dtText;
  906. DoParseFragment;
  907. end;
  908. // XML 1.1 allowed range $10000..$EFFFF is [D800..DB7F] followed by [DC00..DFFF]
  909. function TXMLReader.XML11_CheckName: Boolean;
  910. begin
  911. if (FCurChar >= #$D800) and (FCurChar <= #$DB7F) then
  912. begin
  913. BufAppend(FName, FCurChar);
  914. GetCharRaw;
  915. Result := (FCurChar >= #$DC00) and (FCurChar <= #$DFFF);
  916. end
  917. else
  918. Result := False;
  919. end;
  920. function TXMLReader.CheckName: Boolean;
  921. begin
  922. FName.Length := 0;
  923. Result := (Byte(FCurChar) in NamingBitmap[FNamePages^[hi(Word(FCurChar))]]) or
  924. (FXML11 and XML11_CheckName);
  925. if Result then
  926. repeat
  927. BufAppend(FName, FCurChar);
  928. GetChar;
  929. until not ((Byte(FCurChar) in NamingBitmap[FNamePages^[$100+hi(Word(FCurChar))]]) or
  930. (FXML11 and XML11_CheckName));
  931. end;
  932. function TXMLReader.CheckNmToken: Boolean;
  933. begin
  934. FName.Length := 0;
  935. Result := False;
  936. while (Byte(FCurChar) in NamingBitmap[FNamePages^[$100+hi(Word(FCurChar))]]) or
  937. (FXML11 and XML11_CheckName) do
  938. begin
  939. BufAppend(FName, FCurChar);
  940. GetChar;
  941. Result := True;
  942. end;
  943. end;
  944. procedure TXMLReader.RaiseNameNotFound;
  945. begin
  946. RaiseExc('Name starts with invalid character');
  947. end;
  948. function TXMLReader.ExpectName: WideString;
  949. begin
  950. if not CheckName then
  951. RaiseNameNotFound;
  952. SetString(Result, FName.Buffer, FName.Length);
  953. end;
  954. procedure TXMLReader.SkipName;
  955. begin
  956. if not CheckName then
  957. RaiseNameNotFound;
  958. end;
  959. function TXMLReader.ResolvePredefined(const RefName: WideString): WideChar;
  960. begin
  961. if RefName = 'amp' then
  962. Result := '&'
  963. else if RefName = 'apos' then
  964. Result := ''''
  965. else if RefName = 'gt' then
  966. Result := '>'
  967. else if RefName = 'lt' then
  968. Result := '<'
  969. else if RefName = 'quot' then
  970. Result := '"'
  971. else
  972. Result := #0;
  973. end;
  974. function TXMLReader.ParseCharRef: Boolean; // [66]
  975. var
  976. Value: Integer;
  977. begin
  978. Result := FCurChar = '#';
  979. if Result then
  980. begin
  981. GetCharRaw;
  982. Value := 0;
  983. if CheckForChar('x') then
  984. repeat
  985. case FCurChar of
  986. '0'..'9': Value := Value * 16 + Ord(FCurChar) - Ord('0');
  987. 'a'..'f': Value := Value * 16 + Ord(FCurChar) - (Ord('a') - 10);
  988. 'A'..'F': Value := Value * 16 + Ord(FCurChar) - (Ord('A') - 10);
  989. else
  990. Break;
  991. end;
  992. GetCharRaw;
  993. until False
  994. else
  995. repeat
  996. case FCurChar of
  997. '0'..'9': Value := Value * 10 + Ord(FCurChar) - Ord('0');
  998. else
  999. Break;
  1000. end;
  1001. GetCharRaw;
  1002. until False;
  1003. ExpectChar(';');
  1004. case Value of
  1005. $01..$08, $0B..$0C, $0E..$1F:
  1006. if FXML11 then
  1007. BufAppend(FValue, WideChar(Value))
  1008. else
  1009. RaiseExc('Invalid character reference');
  1010. $09, $0A, $0D, $20..$D7FF, $E000..$FFFD:
  1011. BufAppend(FValue, WideChar(Value));
  1012. $10000..$10FFFF:
  1013. begin
  1014. BufAppend(FValue, WideChar($D7C0 + (Value shr 10)));
  1015. BufAppend(FValue, WideChar($DC00 xor (Value and $3FF)));
  1016. end;
  1017. else
  1018. RaiseExc('Invalid character reference');
  1019. end;
  1020. end;
  1021. end;
  1022. procedure TXMLReader.DoParseAttValue(Delim: WideChar);
  1023. var
  1024. RefNode: TDOMEntityEx;
  1025. begin
  1026. FValue.Length := 0;
  1027. while (FCurChar <> Delim) and (FCurChar <> #0) do
  1028. begin
  1029. if FCurChar = '<' then
  1030. RaiseExc('Literal "<" in attribute value')
  1031. else if FCurChar <> '&' then
  1032. begin
  1033. if FWhitespace then
  1034. FCurChar := #32;
  1035. BufAppend(FValue, FCurChar);
  1036. GetCharRaw;
  1037. end
  1038. else
  1039. begin
  1040. GetCharRaw; // skip '&'
  1041. if ParseCharRef then
  1042. Continue;
  1043. RefNode := ParseReference;
  1044. if Assigned(RefNode) then
  1045. begin
  1046. if FValue.Length > 0 then
  1047. begin
  1048. FCursor.AppendChild(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length));
  1049. FValue.Length := 0;
  1050. end;
  1051. if RefNode.SystemID <> '' then
  1052. RaiseExc('External entity reference is not allowed in attribute value');
  1053. IncludeEntity(RefNode, True);
  1054. end;
  1055. end;
  1056. end; // while
  1057. if FValue.Length > 0 then
  1058. begin
  1059. FCursor.AppendChild(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length));
  1060. FValue.Length := 0;
  1061. end;
  1062. end;
  1063. procedure TXMLReader.DoParseFragment;
  1064. begin
  1065. ParseContent;
  1066. if FCurChar <> #0 then
  1067. RaiseExc('Closing tag not allowed here');
  1068. end;
  1069. function TXMLReader.ContextPush(AEntity: TDOMEntityEx): Boolean;
  1070. var
  1071. Src: TXMLInputSource;
  1072. begin
  1073. if AEntity.SystemID <> '' then
  1074. begin
  1075. Result := ResolveEntity(AEntity.SystemID, AEntity.PublicID, Src);
  1076. if not Result then
  1077. Exit;
  1078. {
  1079. TODO: need different handling of TextDecl in external PEs
  1080. it cannot be parsed if PE is referenced INSIDE declaration
  1081. But - is such case ever met in the wild ?? E.g. MSXML fails such things...
  1082. }
  1083. FAllowedDecl := dtText;
  1084. end
  1085. else
  1086. Src := TXMLInputSource.Create(AEntity.FReplacementText);
  1087. AEntity.FOnStack := True;
  1088. Src.FEntity := AEntity;
  1089. Src.FParent := FSource;
  1090. Src.FCursor := FCursor;
  1091. Unget(FCurChar); // remember FCurChar in previous context
  1092. Inc(FEntityLevel);
  1093. Initialize(Src);
  1094. Result := True;
  1095. end;
  1096. function TXMLReader.ContextPop: Boolean;
  1097. var
  1098. Src: TXMLInputSource;
  1099. begin
  1100. Result := Assigned(FSource.FParent);
  1101. if Result then
  1102. begin
  1103. Src := FSource.FParent;
  1104. if Assigned(FSource.FEntity) then
  1105. TDOMEntityEx(FSource.FEntity).FOnStack := False;
  1106. FCursor := TDOMNode(FSource.FCursor);
  1107. FSource.Free;
  1108. FSource := Src;
  1109. Dec(FEntityLevel);
  1110. GetChar; // re-classify - case of "%pe1;%pe2;"
  1111. end;
  1112. end;
  1113. procedure TXMLReader.IncludeEntity(AEntity: TDOMEntityEx; InAttr: Boolean);
  1114. var
  1115. Node, Child: TDOMNode;
  1116. begin
  1117. if not AEntity.FResolved then
  1118. begin
  1119. if AEntity.FOnStack then
  1120. RaiseExc('Entity ''%s'' recursively references itself', [AEntity.NodeName]);
  1121. if ContextPush(AEntity) then
  1122. begin
  1123. GetCharRaw;
  1124. CheckForChar(#$FEFF);
  1125. FCursor := AEntity; // build child node tree for the entity
  1126. try
  1127. if InAttr then
  1128. DoParseAttValue(#0)
  1129. else
  1130. DoParseFragment;
  1131. AEntity.FResolved := True;
  1132. finally
  1133. ContextPop; // FCursor restored
  1134. FValue.Length := 0;
  1135. end;
  1136. end;
  1137. end;
  1138. Node := FCursor;
  1139. if FCreateEntityRefs or (not AEntity.FResolved) then
  1140. begin
  1141. Node := doc.CreateEntityReference(AEntity.NodeName);
  1142. FCursor.AppendChild(Node);
  1143. end;
  1144. Child := AEntity.FirstChild; // clone the entity node tree
  1145. while Assigned(Child) do
  1146. begin
  1147. Node.AppendChild(Child.CloneNode(True));
  1148. Child := Child.NextSibling;
  1149. end;
  1150. end;
  1151. procedure TXMLReader.StartPE;
  1152. var
  1153. PEName: WideString;
  1154. PEnt: TDOMEntityEx;
  1155. begin
  1156. SetString(PEName, FName.Buffer, FName.Length);
  1157. PEnt := FDocType.PEMap.GetNamedItem(PEName) as TDOMEntityEx;
  1158. if PEnt = nil then // TODO -cVC: Referencing undefined PE
  1159. begin // (These are classified as 'optional errors'...)
  1160. // ValidationError('Undefined parameter entity referenced: %s', [PEName]);
  1161. Exit;
  1162. end;
  1163. if PEnt.FOnStack then
  1164. RaiseExc('Entity ''%%%s'' recursively references itself', [PEnt.NodeName]);
  1165. ContextPush(PEnt);
  1166. end;
  1167. function TXMLReader.ParseReference: TDOMEntityEx;
  1168. var
  1169. RefName: WideString;
  1170. Predef: WideChar;
  1171. begin
  1172. Result := nil;
  1173. RefName := ExpectName;
  1174. ExpectChar(';');
  1175. Predef := ResolvePredefined(RefName);
  1176. if Predef <> #0 then
  1177. BufAppend(FValue, Predef)
  1178. else
  1179. begin
  1180. if Assigned(FDocType) then
  1181. Result := FDocType.Entities.GetNamedItem(RefName) as TDOMEntityEx;
  1182. if Result = nil then
  1183. begin
  1184. if FStandalone or (FDocType = nil) or not (FDocType.HasPERefs or (FDocType.SystemID <> '')) then
  1185. RaiseExc('Undefined entity ''%s'' referenced', [RefName])
  1186. else
  1187. ValidationError('Undefined entity ''%s'' referenced', [RefName]);
  1188. end
  1189. else
  1190. begin
  1191. if FStandalone and (not Result.FInternal) then
  1192. RaiseExc('Standalone constraint violation');
  1193. if Result.NotationName <> '' then
  1194. RaiseExc('Reference to unparsed entity ''%s''', [RefName]);
  1195. end;
  1196. end;
  1197. end;
  1198. procedure TXMLReader.ProcessTextAndRefs;
  1199. var
  1200. nonWs: Boolean;
  1201. last: WideChar;
  1202. RefNode: TDOMEntityEx;
  1203. begin
  1204. FValue.Length := 0;
  1205. nonWs := False;
  1206. FAllowedDecl := dtNone;
  1207. while (FCurChar <> '<') and (FCurChar <> #0) do
  1208. begin
  1209. if FCurChar <> '&' then
  1210. begin
  1211. if not FWhitespace then
  1212. nonWs := True;
  1213. BufAppend(FValue, FCurChar);
  1214. if FCurChar = '>' then
  1215. with FValue do
  1216. if (Length >= 3) and
  1217. (Buffer[Length-2] = ']') and (Buffer[Length-3] = ']') then
  1218. RaiseExc('Literal '']]>'' is not allowed in text');
  1219. GetCharRaw;
  1220. end
  1221. else
  1222. begin
  1223. GetCharRaw; // skip '&'
  1224. if ParseCharRef then
  1225. begin
  1226. last := FValue.Buffer[FValue.Length-1];
  1227. if (last <> #9) and (last <> #10) and (last <> #13) and (last <> #32) then
  1228. nonWs := True;
  1229. Continue;
  1230. end;
  1231. nonWs := True;
  1232. RefNode := ParseReference;
  1233. if Assigned(RefNode) then
  1234. begin
  1235. if (nonWs or FPreserveWhitespace) and (FValue.Length > 0) then
  1236. begin
  1237. FCursor.AppendChild(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length));
  1238. FValue.Length := 0;
  1239. nonWs := False;
  1240. end;
  1241. IncludeEntity(RefNode, False);
  1242. end;
  1243. end;
  1244. end; // while
  1245. if (nonWs or FPreserveWhitespace) and (FValue.Length > 0) then
  1246. begin
  1247. FCursor.AppendChild(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length));
  1248. FValue.Length := 0;
  1249. end;
  1250. end;
  1251. procedure TXMLReader.ExpectAttValue; // [10]
  1252. var
  1253. Delim: WideChar;
  1254. begin
  1255. if (FCurChar <> '''') and (FCurChar <> '"') then
  1256. RaiseExpectedQmark;
  1257. Delim := FCurChar;
  1258. GetCharRaw; // skip quote
  1259. DoParseAttValue(Delim);
  1260. GetChar; // NOTE: not GetCharRaw - when parsing AttDef in DTD,
  1261. // immediately following PERef must be recognized
  1262. end;
  1263. function TXMLReader.SkipQuotedLiteral: Boolean;
  1264. var
  1265. Delim: WideChar;
  1266. begin
  1267. Result := (FCurChar = '''') or (FCurChar = '"');
  1268. if Result then
  1269. begin
  1270. Delim := FCurChar;
  1271. GetCharRaw; // skip quote
  1272. FValue.Length := 0;
  1273. while (FCurChar <> Delim) and (FCurChar <> #0) do
  1274. begin
  1275. BufAppend(FValue, FCurChar);
  1276. GetCharRaw;
  1277. end;
  1278. ExpectChar(Delim); // <-- to check the EOF only
  1279. end;
  1280. end;
  1281. procedure TXMLReader.SkipPubidLiteral; // [12]
  1282. var
  1283. I: Integer;
  1284. begin
  1285. if SkipQuotedLiteral then
  1286. begin
  1287. for I := 0 to FValue.Length-1 do
  1288. if (FValue.Buffer[I] > #255) or not (Char(FValue.Buffer[I]) in PubidChars) then
  1289. RaiseExc('Illegal Public ID literal')
  1290. end
  1291. else
  1292. RaiseExpectedQMark;
  1293. end;
  1294. procedure TXMLReader.SkipSystemLiteral(out Literal: WideString; Required: Boolean);
  1295. begin
  1296. if SkipQuotedLiteral then
  1297. SetString(Literal, FValue.Buffer, FValue.Length)
  1298. else if Required then
  1299. RaiseExpectedQMark;
  1300. end;
  1301. procedure TXMLReader.ParseComment; // [15]
  1302. begin
  1303. ExpectString('--');
  1304. FValue.Length := 0;
  1305. repeat
  1306. BufAppend(FValue, FCurChar);
  1307. GetCharRaw;
  1308. with FValue do
  1309. if (Length >= 2) and (Buffer[Length-1] = '-') and
  1310. (Buffer[Length-2] = '-') then
  1311. begin
  1312. Dec(Length, 2);
  1313. if Assigned(FCursor) then
  1314. FCursor.AppendChild(doc.CreateCommentBuf(Buffer, Length));
  1315. ExpectChar('>');
  1316. Exit;
  1317. end;
  1318. until FCurChar = #0;
  1319. RaiseExc('Unterminated comment');
  1320. end;
  1321. procedure TXMLReader.ParsePI; // [16]
  1322. var
  1323. Name, Value: WideString;
  1324. begin
  1325. GetCharRaw; // skip '?'
  1326. Name := ExpectName;
  1327. with FName do
  1328. if (Length = 3) and
  1329. ((Buffer[0] = 'X') or (Buffer[0] = 'x')) and
  1330. ((Buffer[1] = 'M') or (Buffer[1] = 'm')) and
  1331. ((Buffer[2] = 'L') or (Buffer[2] = 'l')) then
  1332. begin
  1333. if Name <> 'xml' then
  1334. RaiseExc('''xml'' is a reserved word; it must be lowercase');
  1335. if FAllowedDecl <> dtNone then
  1336. begin
  1337. ParseXmlOrTextDecl(FAllowedDecl = dtText);
  1338. FAllowedDecl := dtNone;
  1339. Exit;
  1340. end
  1341. else
  1342. RaiseExc('XML declaration not allowed here');
  1343. end;
  1344. if FCurChar <> '?' then
  1345. ExpectWhitespace;
  1346. FAllowedDecl := dtNone;
  1347. FValue.Length := 0;
  1348. repeat
  1349. BufAppend(FValue, FCurChar);
  1350. GetCharRaw;
  1351. with FValue do
  1352. if (Length >= 2) and (Buffer[Length-1] = '>') and
  1353. (Buffer[Length-2] = '?') then
  1354. begin
  1355. Dec(Length, 2);
  1356. SetString(Value, Buffer, Length);
  1357. if Assigned(FCursor) then
  1358. FCursor.AppendChild(Doc.CreateProcessingInstruction(Name, Value));
  1359. Exit;
  1360. end;
  1361. until FCurChar = #0;
  1362. RaiseExc('Unterminated processing instruction');
  1363. end;
  1364. // here we come from ParsePI, 'xml' is already consumed
  1365. procedure TXMLReader.ParseXmlOrTextDecl(TextDecl: Boolean);
  1366. var
  1367. TmpStr: WideString;
  1368. IsXML11: Boolean;
  1369. begin
  1370. ExpectWhitespace;
  1371. // VersionInfo: optional in TextDecl, required in XmlDecl
  1372. if (not TextDecl) or (FCurChar = 'v') then
  1373. begin
  1374. ExpectString('version'); // [24]
  1375. ExpectEq;
  1376. SkipSystemLiteral(TmpStr, True);
  1377. IsXML11 := False;
  1378. if TmpStr = '1.1' then // Checking for bad chars is implied
  1379. IsXML11 := True
  1380. else if TmpStr <> '1.0' then
  1381. RaiseExc('Illegal version number');
  1382. if not TextDecl then
  1383. begin
  1384. if doc.InheritsFrom(TXMLDocument) then
  1385. TXMLDocument(doc).XMLVersion := TmpStr;
  1386. if IsXML11 then
  1387. XML11_BuildTables;
  1388. end
  1389. else // parsing external entity
  1390. if IsXML11 and not FXML11 then
  1391. RaiseExc('XML 1.0 document cannot invoke XML 1.1 entities');
  1392. if FCurChar <> '?' then
  1393. ExpectWhitespace;
  1394. end;
  1395. // EncodingDecl: required in TextDecl, optional in XmlDecl
  1396. if TextDecl or (FCurChar = 'e') then // [80]
  1397. begin
  1398. ExpectString('encoding');
  1399. ExpectEq;
  1400. SkipSystemLiteral(TmpStr, True);
  1401. if not IsValidEncName(TmpStr) then
  1402. RaiseExc('Illegal encoding name');
  1403. FSource.SetEncoding(TmpStr); // <-- Wide2Ansi conversion here
  1404. // getting here means that specified encoding is supported
  1405. // TODO: maybe assign the 'preferred' encoding name?
  1406. if not TextDecl and doc.InheritsFrom(TXMLDocument) then
  1407. TXMLDocument(doc).Encoding := TmpStr;
  1408. if FCurChar <> '?' then
  1409. ExpectWhitespace;
  1410. end;
  1411. // SDDecl: forbidden in TextDecl, optional in XmlDecl
  1412. if (not TextDecl) and (FCurChar = 's') then
  1413. begin
  1414. ExpectString('standalone');
  1415. ExpectEq;
  1416. SkipSystemLiteral(TmpStr, True);
  1417. if TmpStr = 'yes' then
  1418. FStandalone := True
  1419. else if TmpStr <> 'no' then
  1420. RaiseExc('Only "yes" or "no" are permitted as values of "standalone"');
  1421. SkipWhitespace;
  1422. end;
  1423. ExpectString('?>');
  1424. end;
  1425. procedure TXMLReader.ParseDoctypeDecl; // [28]
  1426. var
  1427. IntSubset: TWideCharBuf;
  1428. Src, OldSrc: TXMLInputSource;
  1429. begin
  1430. FAllowedDecl := dtNone;
  1431. if FDtdParsed then
  1432. RaiseExc('Markup declaration not allowed here');
  1433. ExpectString('DOCTYPE'); // gives possibly incorrect error message
  1434. ExpectWhitespace;
  1435. FDocType := TDOMDocumentTypeEx.Create(doc);
  1436. FDtdParsed := True;
  1437. { To comply with certain output tests, we must insert PIs coming from internal
  1438. subset before DocType node. This looks very synthetic, but let it be...
  1439. Moreover, this code actually duplicates such PIs }
  1440. try
  1441. FDocType.FName := ExpectName;
  1442. ExpectWhitespace;
  1443. ParseExternalID(FDocType.FSystemID, FDocType.FPublicID, False);
  1444. SkipWhitespace;
  1445. if FCurChar = '[' then
  1446. begin
  1447. BufAllocate(IntSubset, 256);
  1448. FCopyBuf := @IntSubset;
  1449. GetChar; // cause very first char after '[' to be appended
  1450. try
  1451. FIntSubset := True;
  1452. ParseMarkupDecl;
  1453. if IntSubset.Length > 0 then // sanity check - must at least contain ']'
  1454. SetString(FDocType.FInternalSubset, IntSubset.Buffer, IntSubset.Length-1);
  1455. ExpectChar(']');
  1456. finally
  1457. FIntSubset := False;
  1458. FCopyBuf := nil;
  1459. FreeMem(IntSubset.Buffer);
  1460. end;
  1461. SkipWhitespace;
  1462. end;
  1463. ExpectChar('>');
  1464. if FDocType.SystemID <> '' then
  1465. begin
  1466. if ResolveEntity(FDocType.SystemID, FDocType.PublicID, Src) then
  1467. begin
  1468. OldSrc := FSource;
  1469. Unget(FCurChar);
  1470. FCursor := nil;
  1471. try
  1472. DoParseExtSubset(Src);
  1473. finally
  1474. while ContextPop do; // Cleanup after possible exceptions
  1475. FSource.Free;
  1476. FSource := OldSrc;
  1477. GetChar;
  1478. FCursor := Doc;
  1479. end;
  1480. end;
  1481. end;
  1482. finally
  1483. doc.AppendChild(FDocType);
  1484. end;
  1485. end;
  1486. procedure TXMLReader.ParseMisc;
  1487. begin
  1488. repeat
  1489. if SkipWhitespace then
  1490. FAllowedDecl := dtNone;
  1491. if not CheckForChar('<') then
  1492. Break;
  1493. if CheckForChar('!') then
  1494. begin
  1495. FAllowedDecl := dtNone;
  1496. if FCurChar = '-' then
  1497. ParseComment
  1498. else
  1499. ParseDoctypeDecl;
  1500. end
  1501. else
  1502. if FCurChar = '?' then
  1503. ParsePI
  1504. else
  1505. Break;
  1506. until FCurChar = #0;
  1507. FAllowedDecl := dtNone;
  1508. end;
  1509. function TXMLReader.ParseEq: Boolean; // [25]
  1510. begin
  1511. while FWhitespace do GetCharRaw;
  1512. Result := FCurChar = '=';
  1513. if Result then
  1514. begin
  1515. GetCharRaw;
  1516. while FWhitespace do GetCharRaw;
  1517. end;
  1518. end;
  1519. procedure TXMLReader.ExpectEq;
  1520. begin
  1521. if not ParseEq then
  1522. RaiseExc('Expected "="');
  1523. end;
  1524. { DTD stuff }
  1525. procedure TXMLReader.AssertPENesting(CurrentLevel: Integer);
  1526. begin
  1527. if CurrentLevel <> FEntityLevel then
  1528. ValidationError('Parameter entities must be properly nested', []);
  1529. end;
  1530. // content model
  1531. type
  1532. TElementContentType = (
  1533. ctEmpty,
  1534. ctAny,
  1535. ctMixed,
  1536. ctName,
  1537. ctChoice,
  1538. ctSeq
  1539. );
  1540. TElementContentQuant = (
  1541. cqNone,
  1542. cqOpt,
  1543. cqReq,
  1544. cqPlus
  1545. );
  1546. {
  1547. TElementContent = record
  1548. ContentType: TElementContentType;
  1549. ContentQuant: TElementContentQuant;
  1550. Name: WideString;
  1551. Children: array of TElementContent;
  1552. end;
  1553. }
  1554. procedure TXMLReader.ExpectChoiceOrSeq(); // [49], [50]
  1555. var
  1556. Delim: WideChar;
  1557. PELevel: Integer;
  1558. begin
  1559. Delim := #0;
  1560. repeat
  1561. SkipWhitespace;
  1562. if FCurChar = '(' then
  1563. begin
  1564. PELevel := FEntityLevel;
  1565. GetChar;
  1566. ExpectChoiceOrSeq;
  1567. AssertPENesting(PELevel);
  1568. GetChar;
  1569. end
  1570. else
  1571. SkipName;
  1572. if CheckForChar('?') then
  1573. else if CheckForChar('*') then
  1574. else if CheckForChar('+') then;
  1575. SkipWhitespace;
  1576. if FCurChar = ')' then
  1577. Break;
  1578. if Delim = #0 then
  1579. begin
  1580. if (FCurChar = '|') or (FCurChar = ',') then
  1581. Delim := FCurChar
  1582. else
  1583. RaiseExc('Expected "|" or ","');
  1584. end
  1585. else
  1586. if FCurChar <> Delim then
  1587. RaiseExc(Delim);
  1588. GetChar; // skip delimiter
  1589. until False;
  1590. end;
  1591. procedure TXMLReader.ParseMixedOrChildren;
  1592. var
  1593. PELevel: Integer;
  1594. NeedAsterisk: Boolean;
  1595. begin
  1596. PELevel := FEntityLevel;
  1597. GetChar; // starting bracket
  1598. SkipWhitespace;
  1599. if CheckForChar('#') then // Mixed section [51]
  1600. begin
  1601. ExpectString('PCDATA');
  1602. SkipWhitespace;
  1603. NeedAsterisk := False;
  1604. while FCurChar <> ')' do
  1605. begin
  1606. ExpectChar('|');
  1607. NeedAsterisk := True;
  1608. SkipWhitespace;
  1609. SkipName;
  1610. SkipWhitespace;
  1611. end;
  1612. AssertPENesting(PELevel);
  1613. GetChar;
  1614. if NeedAsterisk then
  1615. ExpectChar('*')
  1616. else
  1617. CheckForChar('*');
  1618. end
  1619. else // Parse Children section [47]
  1620. begin
  1621. ExpectChoiceOrSeq;
  1622. AssertPENesting(PELevel);
  1623. GetChar;
  1624. if CheckForChar('?') then
  1625. else if CheckForChar('*') then
  1626. else if CheckForChar('+') then;
  1627. end;
  1628. end;
  1629. procedure TXMLReader.ParseElementDecl; // [45]
  1630. begin
  1631. SkipName;
  1632. ExpectWhitespace;
  1633. // Get contentspec [46]
  1634. if FCurChar = 'E' then
  1635. ExpectString('EMPTY')
  1636. else if FCurChar = 'A' then
  1637. ExpectString('ANY')
  1638. else if FCurChar = '(' then
  1639. ParseMixedOrChildren
  1640. else
  1641. RaiseExc('Invalid content specification');
  1642. end;
  1643. procedure TXMLReader.ParseNotationDecl; // [82]
  1644. var
  1645. Notation: TDOMNotationEx;
  1646. begin
  1647. Notation := TDOMNotationEx(TDOMNotation.Create(Doc));
  1648. try
  1649. Notation.FName := ExpectName;
  1650. ExpectWhitespace;
  1651. if not ParseExternalID(Notation.FSystemID, Notation.FPublicID, True) then
  1652. RaiseExc('Expected external or public ID');
  1653. except
  1654. Notation.Free;
  1655. raise;
  1656. end;
  1657. if FDocType.Notations.GetNamedItem(Notation.FName) = nil then
  1658. FDocType.Notations.SetNamedItem(Notation)
  1659. else
  1660. begin
  1661. ValidationError('Duplicate notation declaration: %s', [Notation.FName]);
  1662. Notation.Free;
  1663. end;
  1664. end;
  1665. procedure TXMLReader.ParseAttlistDecl; // [52]
  1666. var
  1667. SaveCurNode: TDOMNode;
  1668. ValueRequired: Boolean;
  1669. Token: WideString;
  1670. ElDef: TDOMElementDef;
  1671. AttDef: TDOMAttrDef;
  1672. begin
  1673. Token := ExpectName;
  1674. ElDef := TDOMElementDef(FDocType.ElementDefs.GetNamedItem(Token));
  1675. if ElDef = nil then
  1676. begin
  1677. // TODO -cVC: must distinguish ElementDef created here from one explicitly declared
  1678. ElDef := TDOMElementDef.Create(doc);
  1679. ElDef.FNodeName := Token;
  1680. FDocType.ElementDefs.SetNamedItem(ElDef);
  1681. end;
  1682. SkipWhitespace;
  1683. while FCurChar <> '>' do
  1684. begin
  1685. SkipWhitespace; { !!! }
  1686. AttDef := TDOMAttrDef.Create(doc);
  1687. try
  1688. AttDef.FName := ExpectName;
  1689. ExpectWhitespace;
  1690. Token := GetString(['A'..'Z']); // Get AttType [54], [55], [56]
  1691. if Token = 'CDATA' then
  1692. AttDef.FDataType := DT_CDATA
  1693. else if Token = 'ID' then
  1694. AttDef.FDataType := DT_ID
  1695. else if Token = 'IDREF' then
  1696. AttDef.FDataType := DT_IDREF
  1697. else if Token = 'IDREFS' then
  1698. AttDef.FDataType := DT_IDREFS
  1699. else if Token = 'ENTITY' then
  1700. AttDef.FDataType := DT_ENTITY
  1701. else if Token = 'ENTITIES' then
  1702. AttDef.FDataType := DT_ENTITIES
  1703. else if Token = 'NMTOKEN' then
  1704. AttDef.FDataType := DT_NMTOKEN
  1705. else if Token = 'NMTOKENS' then
  1706. AttDef.FDataType := DT_NMTOKENS
  1707. else if Token = 'NOTATION' then // [57], [58]
  1708. begin
  1709. AttDef.FDataType := DT_NOTATION;
  1710. ExpectWhitespace;
  1711. ExpectChar('(');
  1712. repeat
  1713. SkipWhitespace;
  1714. SkipName;
  1715. SkipWhitespace;
  1716. until not CheckForChar('|');
  1717. ExpectChar(')');
  1718. end
  1719. else
  1720. if CheckForChar('(') then // [59]
  1721. begin
  1722. AttDef.FDataType := DT_NMTOKEN;
  1723. repeat
  1724. SkipWhitespace;
  1725. if not CheckNmToken then
  1726. RaiseNameNotFound; // not completely correct error message
  1727. SkipWhitespace;
  1728. until not CheckForChar('|');
  1729. ExpectChar(')');
  1730. end else
  1731. RaiseExc('Invalid tokenized type');
  1732. ExpectWhitespace;
  1733. // Get DefaultDecl [60]
  1734. ValueRequired := False;
  1735. if CheckForChar('#') then
  1736. begin
  1737. Token := GetString(['A'..'Z']);
  1738. if Token = 'REQUIRED' then
  1739. AttDef.FDefault := AD_REQUIRED
  1740. else if Token = 'IMPLIED' then
  1741. AttDef.FDefault := AD_IMPLIED
  1742. else if Token = 'FIXED' then
  1743. begin
  1744. AttDef.FDefault := AD_FIXED;
  1745. ExpectWhitespace;
  1746. ValueRequired := True;
  1747. end
  1748. else
  1749. RaiseExc('Illegal attribute default');
  1750. end
  1751. else
  1752. begin
  1753. AttDef.FDefault := AD_DEFAULT;
  1754. ValueRequired := True;
  1755. end;
  1756. if ValueRequired then
  1757. begin
  1758. SaveCurNode := FCursor;
  1759. FCursor := AttDef;
  1760. // tricky moment, no tests for that
  1761. { FRecognizePE := False; } // TODO: shall it really be disabled?
  1762. try
  1763. ExpectAttValue;
  1764. finally
  1765. FCursor := SaveCurNode;
  1766. { FRecognizePE := not FIntSubset; }
  1767. end;
  1768. if AttDef.FDataType = DT_ID then
  1769. ValidationError('Attributes of type ID must not have a default value',[]);
  1770. end;
  1771. // First declaration is binding, subsequent should be ignored
  1772. if Assigned(ElDef.GetAttributeNode(AttDef.Name)) then
  1773. AttDef.Free
  1774. else
  1775. ElDef.SetAttributeNode(AttDef);
  1776. except
  1777. AttDef.Free;
  1778. raise;
  1779. end;
  1780. SkipWhitespace;
  1781. end;
  1782. end;
  1783. procedure TXMLReader.ParseEntityDeclValue(Delim: WideChar); // [9]
  1784. var
  1785. I: Integer;
  1786. Src: TXMLInputSource;
  1787. begin
  1788. Src := FSource;
  1789. // "Included in literal": process until delimiter hit IN SAME context
  1790. while not ((FSource = Src) and CheckForChar(Delim)) do
  1791. if ParsePEReference then
  1792. begin
  1793. if FIntSubset and (FSource.FParent = nil) then
  1794. RaiseExc('PE references in internal subset not allowed inside declarations');
  1795. StartPE;
  1796. GetCharRaw;
  1797. end
  1798. else if FCurChar = '&' then // CharRefs: include, EntityRefs: bypass
  1799. begin
  1800. GetCharRaw;
  1801. if not ParseCharRef then
  1802. begin
  1803. BufAppend(FValue, '&');
  1804. ExpectName;
  1805. ExpectChar(';');
  1806. for I := 0 to FName.Length-1 do
  1807. BufAppend(FValue, FName.Buffer[I]);
  1808. BufAppend(FValue, ';');
  1809. end;
  1810. end
  1811. else if FCurChar <> #0 then // Regular character
  1812. begin
  1813. BufAppend(FValue, FCurChar);
  1814. GetCharRaw;
  1815. end
  1816. else if not ContextPop then // #0
  1817. Break;
  1818. end;
  1819. procedure TXMLReader.ParseEntityDecl; // [70]
  1820. var
  1821. NDataAllowed: Boolean;
  1822. Delim: WideChar;
  1823. Entity: TDOMEntityEx;
  1824. Map: TDOMNamedNodeMap;
  1825. begin
  1826. NDataAllowed := True;
  1827. Map := FDocType.Entities;
  1828. if CheckForChar('%') then // [72]
  1829. begin
  1830. ExpectWhitespace;
  1831. NDataAllowed := False;
  1832. Map := FDocType.PEMap;
  1833. end;
  1834. Entity := TDOMEntityEx.Create(Doc);
  1835. try
  1836. Entity.FInternal := FIntSubset and (FSource.FParent = nil);
  1837. Entity.FName := ExpectName;
  1838. ExpectWhitespace;
  1839. if (FCurChar = '"') or (FCurChar = '''') then
  1840. begin
  1841. NDataAllowed := False;
  1842. Delim := FCurChar;
  1843. FRecognizePE := False; // PERef right after delimiter should not be recognized
  1844. GetCharRaw; // at char level - we process it 'manually'
  1845. FValue.Length := 0;
  1846. ParseEntityDeclValue(Delim);
  1847. FRecognizePE := not FIntSubset;
  1848. SetString(Entity.FReplacementText, FValue.Buffer, FValue.Length);
  1849. end
  1850. else
  1851. if not ParseExternalID(Entity.FSystemID, Entity.FPublicID, False) then
  1852. RaiseExc('Expected entity value or external ID');
  1853. if NDataAllowed then // [76]
  1854. begin
  1855. if FCurChar <> '>' then
  1856. ExpectWhitespace;
  1857. if FCurChar = 'N' then
  1858. begin
  1859. ExpectString('NDATA');
  1860. ExpectWhitespace;
  1861. SkipName;
  1862. // TODO -cVC: Notation declared. Here or after all has been read?
  1863. SetString(Entity.FNotationName, FName.Buffer, FName.Length);
  1864. if FDocType.Notations.GetNamedItem(Entity.NotationName) = nil then
  1865. ValidationError('Reference to undeclared notation ''%s''', [Entity.NotationName]);
  1866. end;
  1867. end;
  1868. except
  1869. Entity.Free;
  1870. raise;
  1871. end;
  1872. // Repeated declarations of same entity are legal but must be ignored
  1873. if Map.GetNamedItem(Entity.NodeName) = nil then
  1874. Map.SetNamedItem(Entity)
  1875. else
  1876. Entity.Free;
  1877. end;
  1878. procedure TXMLReader.ParseMarkupDecl; // [29]
  1879. var
  1880. Token: WideString;
  1881. IncludeLevel: Integer;
  1882. IgnoreLevel: Integer;
  1883. PELevel: Integer;
  1884. begin
  1885. IncludeLevel := 0;
  1886. IgnoreLevel := 0;
  1887. repeat
  1888. if SkipWhitespace then
  1889. FAllowedDecl := dtNone;
  1890. if ParsePEReference then // PERef between declarations should always be recognized
  1891. begin
  1892. FAllowedDecl := dtNone;
  1893. if Assigned(FDocType) then
  1894. FDocType.HasPERefs := True;
  1895. StartPE;
  1896. GetChar;
  1897. Continue;
  1898. end;
  1899. if (FCurChar = #0) and ContextPop then
  1900. Continue;
  1901. if (FCurChar = ']') and (IncludeLevel > 0) then
  1902. begin
  1903. ExpectString(']]>');
  1904. Dec(IncludeLevel);
  1905. Continue;
  1906. end;
  1907. if FCurChar <> '<' then
  1908. Break;
  1909. PELevel := FEntityLevel;
  1910. GetCharRaw;
  1911. if CheckForChar('!') then
  1912. begin
  1913. FAllowedDecl := dtNone;
  1914. if FCurChar = '-' then
  1915. ParseComment
  1916. else if FCurChar = '[' then
  1917. begin
  1918. if FIntSubset and (FSource.FParent = nil) then
  1919. RaiseExc('Conditional sections not allowed in internal subset');
  1920. FRecognizePE := not FIntSubset;
  1921. GetChar; // skip '['
  1922. SkipWhitespace;
  1923. Token := GetString(['A'..'Z']);
  1924. SkipWhitespace;
  1925. if Token = 'INCLUDE' then
  1926. Inc(IncludeLevel)
  1927. else if Token = 'IGNORE' then
  1928. IgnoreLevel := 1
  1929. else
  1930. RaiseExc('Expected "INCLUDE" or "IGNORE"');
  1931. AssertPENesting(PELevel);
  1932. ExpectChar('[');
  1933. if IgnoreLevel > 0 then
  1934. repeat
  1935. FRecognizePE := False; // PEs not recognized in IGNORE section
  1936. if CheckForChar('<') and CheckForChar('!') and CheckForChar('[') then
  1937. Inc(IgnoreLevel)
  1938. else if CheckForChar(']') and CheckForChar(']') and CheckForChar('>') then
  1939. Dec(IgnoreLevel)
  1940. else GetChar;
  1941. until (IgnoreLevel=0) or (FCurChar = #0);
  1942. end
  1943. else
  1944. begin
  1945. FRecognizePE := not FIntSubset;
  1946. Token := GetString(['A'..'Z']);
  1947. ExpectWhitespace;
  1948. if Token = 'ELEMENT' then
  1949. ParseElementDecl
  1950. else if Token = 'ENTITY' then
  1951. ParseEntityDecl
  1952. else if Token = 'ATTLIST' then
  1953. ParseAttlistDecl
  1954. else if Token = 'NOTATION' then
  1955. ParseNotationDecl
  1956. else
  1957. RaiseExc('Illegal markup declaration');
  1958. SkipWhitespace;
  1959. FRecognizePE := False; // ! Don't auto-pop context on last markup delimiter
  1960. ExpectChar('>'); // This enables correct nesting check
  1961. end;
  1962. {
  1963. MarkupDecl starting in PE and ending in root is a WFC [28a]
  1964. MarkupDecl starting in root but ending in PE is a VC (erratum 2e-14)
  1965. }
  1966. if PELevel > FEntityLevel then
  1967. RaiseExc('Parameter entities must be properly nested')
  1968. else
  1969. AssertPENesting(PELevel);
  1970. end
  1971. else if FCurChar = '?' then
  1972. ParsePI;
  1973. until False;
  1974. FRecognizePE := False;
  1975. if (IncludeLevel > 0) or (IgnoreLevel > 0) then
  1976. RaiseExc('Conditional section not closed');
  1977. end;
  1978. procedure TXMLReader.DoParseExtSubset(ASource: TXMLInputSource);
  1979. begin
  1980. InitializeRoot(ASource);
  1981. FAllowedDecl := dtText;
  1982. ParseMarkupDecl;
  1983. if FCurChar <> #0 then
  1984. RaiseExc('Illegal character in DTD');
  1985. end;
  1986. procedure TXMLReader.ProcessDTD(ASource: TXMLInputSource);
  1987. begin
  1988. doc := TXMLDocument.Create;
  1989. FDocType := TDOMDocumentTypeEx.Create(doc);
  1990. // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag
  1991. // TODO: what shall be FCursor? FDocType cannot - it does not accept child nodes
  1992. doc.AppendChild(FDocType);
  1993. DoParseExtSubset(ASource);
  1994. end;
  1995. procedure TXMLReader.ParseCDSect; // [18]
  1996. var
  1997. name: WideString;
  1998. begin
  1999. ExpectString('[CDATA[');
  2000. FValue.Length := 0;
  2001. repeat
  2002. BufAppend(FValue, FCurChar);
  2003. GetCharRaw;
  2004. with FValue do
  2005. if (Length >= 3) and (Buffer[Length-1] = '>') and
  2006. (Buffer[Length-2] = ']') and (Buffer[Length-3] = ']') then
  2007. begin
  2008. Dec(Length, 3);
  2009. SetString(name, Buffer, Length);
  2010. FCursor.AppendChild(doc.CreateCDATASection(name));
  2011. Exit;
  2012. end;
  2013. until FCurChar = #0;
  2014. RaiseExc('Unterminated CDATA section');
  2015. end;
  2016. procedure TXMLReader.ParseContent;
  2017. begin
  2018. repeat
  2019. if FCurChar = '<' then
  2020. begin
  2021. GetCharRaw;
  2022. if CheckName then
  2023. ParseElement
  2024. else if FCurChar = '!' then
  2025. begin
  2026. GetCharRaw;
  2027. FAllowedDecl := dtNone;
  2028. if FCurChar = '[' then
  2029. ParseCDSect
  2030. else if FCurChar = '-' then
  2031. ParseComment
  2032. else
  2033. ParseDoctypeDecl; // actually will raise error
  2034. end
  2035. else if FCurChar = '?' then
  2036. ParsePI
  2037. else
  2038. Exit;
  2039. end
  2040. else
  2041. ProcessTextAndRefs;
  2042. until FCurChar = #0;
  2043. end;
  2044. // Element name already in FNameBuffer
  2045. procedure TXMLReader.ParseElement; // [39] [40] [44]
  2046. var
  2047. NewElem: TDOMElement;
  2048. IsEmpty: Boolean;
  2049. attr, OldAttr: TDOMNode;
  2050. begin
  2051. NewElem := doc.CreateElementBuf(FName.Buffer, FName.Length);
  2052. FCursor.AppendChild(NewElem);
  2053. Assert(NewElem.ParentNode = FCursor, 'AppendChild did not set ParentNode');
  2054. FCursor := NewElem;
  2055. IsEmpty := False;
  2056. while FCurChar <> '>' do
  2057. begin
  2058. if FCurChar = '/' then
  2059. begin
  2060. GetCharRaw;
  2061. IsEmpty := True;
  2062. FCursor := FCursor.ParentNode;
  2063. Break;
  2064. end;
  2065. // Get Attribute [41]
  2066. ExpectWhitespace;
  2067. if not CheckName then // allow stuff like <element >, <element />
  2068. Continue;
  2069. attr := doc.CreateAttributeBuf(FName.Buffer, FName.Length);
  2070. // !!cannot use TDOMElement.SetAttributeNode because it will free old attribute
  2071. OldAttr := NewElem.Attributes.SetNamedItem(Attr);
  2072. if Assigned(OldAttr) then
  2073. begin
  2074. OldAttr.Free;
  2075. RaiseExc('Duplicate attribute');
  2076. end;
  2077. ExpectEq;
  2078. Assert(TDOMAttr(attr).OwnerElement = NewElem, 'DOMAttr.OwnerElement not set correctly');
  2079. FCursor := attr;
  2080. ExpectAttValue;
  2081. FCursor := NewElem;
  2082. end;
  2083. ExpectChar('>');
  2084. ProcessDefaultAttributes(NewElem);
  2085. if not IsEmpty then
  2086. begin
  2087. if not FPreserveWhitespace then // critical for testsuite compliance
  2088. SkipWhitespace;
  2089. ParseContent;
  2090. if FCurChar = '/' then // Get ETag [42]
  2091. begin
  2092. GetCharRaw;
  2093. if ExpectName <> NewElem.TagName then
  2094. RaiseExc('Unmatching element end tag (expected "</%s>")', [NewElem.TagName]);
  2095. SkipWhitespace;
  2096. ExpectChar('>');
  2097. FCursor := FCursor.ParentNode;
  2098. end
  2099. else if FCurChar <> #0 then
  2100. RaiseNameNotFound
  2101. else // End of stream in content
  2102. RaiseExc('Document element not closed');
  2103. end;
  2104. end;
  2105. procedure TXMLReader.ProcessDefaultAttributes(Element: TDOMElement);
  2106. var
  2107. I: Integer;
  2108. ElDef: TDOMElementDef;
  2109. AttDefs: TDOMNamedNodeMap;
  2110. AttDef: TDOMAttrDef;
  2111. Attr: TDOMAttrEx;
  2112. Spec: Boolean;
  2113. begin
  2114. if Assigned(FDocType) then
  2115. begin
  2116. ElDef := TDOMElementDef(FDocType.ElementDefs.GetNamedItem(Element.TagName));
  2117. if Assigned(ElDef) and ElDef.HasAttributes then
  2118. begin
  2119. AttDefs := ElDef.Attributes;
  2120. for I := 0 to AttDefs.Length-1 do
  2121. begin
  2122. AttDef := AttDefs[I] as TDOMAttrDef;
  2123. Spec := True;
  2124. // no validity checking yet; just append default values
  2125. Attr := TDOMAttrEx(Element.GetAttributeNode(AttDef.Name));
  2126. if (AttDef.FDefault in [AD_DEFAULT, AD_FIXED]) and (Attr = nil) then
  2127. begin
  2128. Attr := TDOMAttrEx(AttDef.CloneNode(True));
  2129. Element.SetAttributeNode(Attr);
  2130. Spec := False;
  2131. end;
  2132. if Assigned(Attr) then
  2133. begin
  2134. Attr.FSpecified := Spec;
  2135. Attr.FNormalize := (AttDef.FDataType <> DT_CDATA);
  2136. end;
  2137. end;
  2138. end;
  2139. end;
  2140. end;
  2141. function TXMLReader.ParsePEReference: Boolean; // [69]
  2142. begin
  2143. Result := CheckForChar('%');
  2144. if Result then
  2145. begin
  2146. SkipName;
  2147. ExpectChar(';');
  2148. end;
  2149. end;
  2150. function TXMLReader.ParseExternalID(out SysID, PubID: WideString; // [75]
  2151. SysIdOptional: Boolean): Boolean;
  2152. begin
  2153. if FCurChar = 'S' then
  2154. begin
  2155. ExpectString('SYSTEM');
  2156. ExpectWhitespace;
  2157. SkipSystemLiteral(SysID, True);
  2158. Result := True;
  2159. end
  2160. else if FCurChar = 'P' then
  2161. begin
  2162. ExpectString('PUBLIC');
  2163. ExpectWhitespace;
  2164. SkipPubidLiteral;
  2165. SetString(PubID, FValue.Buffer, FValue.Length);
  2166. if SysIdOptional then
  2167. begin
  2168. SkipWhitespace;
  2169. SkipSystemLiteral(SysID, False);
  2170. end
  2171. else
  2172. begin
  2173. ExpectWhitespace;
  2174. SkipSystemLiteral(SysID, True);
  2175. end;
  2176. Result := True;
  2177. end else
  2178. Result := False;
  2179. end;
  2180. procedure TXMLReader.ValidationError(const Msg: string;
  2181. const args: array of const);
  2182. begin
  2183. // TODO: just a stub now
  2184. FInvalid := True;
  2185. end;
  2186. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text);
  2187. var
  2188. Reader: TXMLReader;
  2189. Src: TXMLInputSource;
  2190. begin
  2191. ADoc := nil;
  2192. Src := TXMLFileInputSource.Create(f);
  2193. Src.SystemID := FilenameToURI(TTextRec(f).Name);
  2194. Reader := TXMLReader.Create;
  2195. try
  2196. Reader.ProcessXML(Src);
  2197. ADoc := TXMLDocument(Reader.Doc);
  2198. finally
  2199. Reader.Free;
  2200. end;
  2201. end;
  2202. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: TStream; const ABaseURI: String);
  2203. var
  2204. Reader: TXMLReader;
  2205. Src: TXMLInputSource;
  2206. begin
  2207. ADoc := nil;
  2208. Reader := TXMLReader.Create;
  2209. try
  2210. Src := TXMLStreamInputSource.Create(f, False);
  2211. Src.SystemID := ABaseURI;
  2212. Reader.ProcessXML(Src);
  2213. finally
  2214. ADoc := TXMLDocument(Reader.doc);
  2215. Reader.Free;
  2216. end;
  2217. end;
  2218. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: TStream);
  2219. begin
  2220. ReadXMLFile(ADoc, f, 'stream:');
  2221. end;
  2222. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String);
  2223. var
  2224. FileStream: TStream;
  2225. begin
  2226. ADoc := nil;
  2227. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  2228. try
  2229. ReadXMLFile(ADoc, FileStream, FilenameToURI(AFilename));
  2230. finally
  2231. FileStream.Free;
  2232. end;
  2233. end;
  2234. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text);
  2235. var
  2236. Reader: TXMLReader;
  2237. Src: TXMLInputSource;
  2238. begin
  2239. Reader := TXMLReader.Create;
  2240. try
  2241. Src := TXMLFileInputSource.Create(f);
  2242. Src.SystemID := FilenameToURI(TTextRec(f).Name);
  2243. Reader.ProcessFragment(Src, AParentNode);
  2244. finally
  2245. Reader.Free;
  2246. end;
  2247. end;
  2248. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream; const ABaseURI: String);
  2249. var
  2250. Reader: TXMLReader;
  2251. Src: TXMLInputSource;
  2252. begin
  2253. Reader := TXMLReader.Create;
  2254. try
  2255. Src := TXMLStreamInputSource.Create(f, False);
  2256. Src.SystemID := ABaseURI;
  2257. Reader.ProcessFragment(Src, AParentNode);
  2258. finally
  2259. Reader.Free;
  2260. end;
  2261. end;
  2262. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream);
  2263. begin
  2264. ReadXMLFragment(AParentNode, f, 'stream:');
  2265. end;
  2266. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String);
  2267. var
  2268. Stream: TStream;
  2269. begin
  2270. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  2271. try
  2272. ReadXMLFragment(AParentNode, Stream, FilenameToURI(AFilename));
  2273. finally
  2274. Stream.Free;
  2275. end;
  2276. end;
  2277. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text);
  2278. var
  2279. Reader: TXMLReader;
  2280. Src: TXMLInputSource;
  2281. begin
  2282. ADoc := nil;
  2283. Reader := TXMLReader.Create;
  2284. try
  2285. Src := TXMLFileInputSource.Create(f);
  2286. Src.SystemID := FilenameToURI(TTextRec(f).Name);
  2287. Reader.ProcessDTD(Src);
  2288. ADoc := TXMLDocument(Reader.doc);
  2289. finally
  2290. Reader.Free;
  2291. end;
  2292. end;
  2293. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream; const ABaseURI: String);
  2294. var
  2295. Reader: TXMLReader;
  2296. Src: TXMLInputSource;
  2297. begin
  2298. ADoc := nil;
  2299. Reader := TXMLReader.Create;
  2300. try
  2301. Src := TXMLStreamInputSource.Create(f, False);
  2302. Src.SystemID := ABaseURI;
  2303. Reader.ProcessDTD(Src);
  2304. ADoc := TXMLDocument(Reader.doc);
  2305. finally
  2306. Reader.Free;
  2307. end;
  2308. end;
  2309. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream);
  2310. begin
  2311. ReadDTDFile(ADoc, f, 'stream:');
  2312. end;
  2313. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String);
  2314. var
  2315. Stream: TStream;
  2316. begin
  2317. ADoc := nil;
  2318. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  2319. try
  2320. ReadDTDFile(ADoc, Stream, FilenameToURI(AFilename));
  2321. finally
  2322. Stream.Free;
  2323. end;
  2324. end;
  2325. end.