Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. (*
  2.     Copyright 2016, 2020, 2022 Anton Krotov
  3.  
  4.     This file is part of fb2read.
  5.  
  6.     fb2read is free software: you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation, either version 3 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     fb2read is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with fb2read. If not, see <http://www.gnu.org/licenses/>.
  18. *)
  19.  
  20. MODULE XML;
  21.  
  22. IMPORT SU := SysUtils, RF := ReadFile, S := Strings, Encode, V := Vector, tables, LISTS;
  23.  
  24.  
  25. CONST
  26.  
  27.     tag_p*                =  1;
  28.     tag_v*                =  2;
  29.     tag_section*          =  3;
  30.     tag_stanza*           =  4;
  31.     tag_empty_line*       =  5;
  32.     tag_subtitle*         =  6;
  33.     tag_date*             =  7;
  34.     tag_text_author*      =  8;
  35.     tag_a*                =  9;
  36.     tag_sub*              = 10;
  37.     tag_sup*              = 11;
  38.     tag_code*             = 12;
  39.     tag_poem*             = 13;
  40.     tag_title*            = 14;
  41.     tag_FictionBook*      = 15;
  42.     tag_body*             = 16;
  43.     tag_strikethrough*    = 17;
  44.     tag_strong*           = 18;
  45.     tag_cite*             = 19;
  46.     tag_epigraph*         = 20;
  47.     tag_emphasis*         = 21;
  48.     tag_image*            = 22;
  49.     tag_binary*           = 23;
  50.     tag_coverpage*        = 24;
  51.     tag_description*      = 25;
  52.     tag_xml*              = 26;
  53.     tag_annotation*       = 27;
  54.     tag_contents_item*    = 28;
  55.     tag_table*            = 29;
  56.     tag_tr*               = 30;
  57.     tag_td*               = 31;
  58.     tag_th*               = 32;
  59.     tag_unknown*          = -1;
  60.  
  61.  
  62. TYPE
  63.  
  64.   ELEMENT* = POINTER TO DESC_ELEMENT;
  65.  
  66.   TEXT*    = POINTER TO DESC_TEXT;
  67.  
  68.   SPACE*   = POINTER TO DESC_SPACE;
  69.  
  70.   WORD*    = POINTER TO DESC_WORD;
  71.  
  72.   TAG*     = POINTER TO DESC_TAG;
  73.  
  74.   ATTR*    = POINTER TO DESC_ATTR;
  75.  
  76.   TAG_ID   = POINTER TO DESC_TAG_ID;
  77.  
  78.  
  79.   LIST*  = RECORD first*, last* : ELEMENT END;
  80.  
  81.   DESC_ELEMENT* = RECORD (V.ANYREC)
  82.     parent*, next* : ELEMENT
  83.   END;
  84.  
  85.   DESC_TEXT = RECORD (DESC_ELEMENT)
  86.     X*, Y*  : INTEGER;
  87.     width*  : INTEGER
  88.   END;
  89.  
  90.   DESC_SPACE = RECORD (DESC_TEXT)
  91.  
  92.   END;
  93.  
  94.   DESC_WORD = RECORD (DESC_TEXT)
  95.     length* : INTEGER;
  96.     value*  : S.CHARS
  97.   END;
  98.  
  99.   DESC_TAG = RECORD (DESC_ELEMENT)
  100.     name*    : S.CHARS;
  101.     value*   : INTEGER;
  102.     child*   : LIST;
  103.     attr*    : LIST;
  104.     Ymin*    : INTEGER;
  105.     Ymax*    : INTEGER;
  106.     X*       : INTEGER;
  107.     Width*   : INTEGER;
  108.     Clicked* : BOOLEAN;
  109.     Visited* : BOOLEAN;
  110.     img*     : INTEGER;
  111.     num*     : INTEGER;
  112.     cell*    : INTEGER;
  113.     table*   : tables.Table;
  114.     text*    : LISTS.ITEM
  115.   END;
  116.  
  117.   DESC_ATTR = RECORD (DESC_ELEMENT)
  118.     name  : S.CHARS;
  119.     value : S.CHARS
  120.   END;
  121.  
  122.   DESC_TAG_ID = RECORD (DESC_ELEMENT)
  123.     tag : TAG;
  124.     id  : S.CHARS
  125.   END;
  126.  
  127.  
  128. VAR
  129.     ch: CHAR; binary: BOOLEAN;
  130.  
  131.     Root, Current, Header, FB*: ELEMENT;
  132.  
  133.     Tag_id: LIST;
  134.  
  135.     tire1, tire2, nbsp, ellipsis, apo,
  136.     quot1, quot2, quot3, quot4, quot5, quot6, quot7,
  137.     number, bullet, euro,
  138.     dash1, dash2: S.UTF8;
  139.  
  140.     num: INTEGER;
  141.     Tags: V.VECTOR;
  142.  
  143.  
  144. PROCEDURE GetTagByNum*(n: INTEGER): TAG;
  145. VAR ptr: V.ANYPTR;
  146. BEGIN
  147.   ptr := V.get(Tags, n)
  148.   RETURN ptr(TAG)
  149. END GetTagByNum;
  150.  
  151.  
  152. PROCEDURE ListCount*(list: LIST): INTEGER;
  153. VAR cur: ELEMENT; res: INTEGER;
  154. BEGIN
  155.   res := 0;
  156.   cur := list.first;
  157.   WHILE cur # NIL DO
  158.     INC(res);
  159.     cur := cur.next
  160.   END
  161.   RETURN res
  162. END ListCount;
  163.  
  164.  
  165. PROCEDURE GetTagByID(id: S.CHARS): TAG;
  166. VAR
  167.   cur    : TAG_ID;
  168.   Result : TAG;
  169. BEGIN
  170.   Result := NIL;
  171.   cur := Tag_id.first(TAG_ID);
  172.   WHILE cur # NIL DO
  173.     IF S.CharsEq(id, cur.id) THEN
  174.       Result := cur.tag;
  175.       cur := NIL
  176.     ELSE
  177.       cur := cur.next(TAG_ID)
  178.     END
  179.   END
  180.   RETURN Result
  181. END GetTagByID;
  182.  
  183.  
  184. PROCEDURE GetAttr*(tag: TAG; attr_name: S.STRING; VAR attr_value: S.CHARS): BOOLEAN;
  185. VAR attr: ELEMENT;
  186.     found: BOOLEAN;
  187. BEGIN
  188.   found := FALSE;
  189.   attr := tag.attr.first;
  190.   WHILE ~found & (attr # NIL) DO
  191.     IF S.CharsEqStr(attr(ATTR).name, attr_name) THEN
  192.       attr_value := attr(ATTR).value;
  193.       INC(attr_value.first);
  194.       DEC(attr_value.last);
  195.       found := TRUE
  196.     ELSE
  197.       attr := attr.next
  198.     END
  199.   END
  200.   RETURN found
  201. END GetAttr;
  202.  
  203.  
  204. PROCEDURE IsHref(attr_name: S.CHARS): BOOLEAN;
  205. VAR chars: S.CHARS;
  206. BEGIN
  207.   chars := attr_name;
  208.   chars.first := chars.last - 4
  209.   RETURN S.CharsEqStr(chars, ":href")
  210. END IsHref;
  211.  
  212.  
  213. PROCEDURE GetRef*(tag: TAG; VAR note: BOOLEAN; VAR URL: INTEGER): TAG;
  214. VAR
  215.   attr   : ATTR;
  216.   chars  : S.CHARS;
  217.   Result : TAG;
  218. BEGIN
  219.   Result := NIL;
  220.   note := FALSE;
  221.   URL := 0;
  222.   attr := tag.attr.first(ATTR);
  223.   WHILE attr # NIL DO
  224.     IF IsHref(attr.name) THEN
  225.       chars := attr.value;
  226.       INC(chars.first);
  227.       IF S.GetChar(chars, 0) = "#" THEN
  228.         DEC(chars.last);
  229.         INC(chars.first);
  230.         Result := GetTagByID(chars)
  231.       ELSE
  232.         S.PutChar(chars, chars.last - chars.first, 0X);
  233.         URL := chars.first
  234.       END
  235.     ELSIF S.CharsEqStr(attr.name, "type") THEN
  236.       chars := attr.value;
  237.       INC(chars.first);
  238.       DEC(chars.last);
  239.       note := S.CharsEqStr(chars, "note")
  240.     END;
  241.     attr := attr.next(ATTR)
  242.   END
  243.   RETURN Result
  244. END GetRef;
  245.  
  246.  
  247. PROCEDURE IsNote*(tag: TAG): BOOLEAN;
  248. VAR
  249.   res  : TAG;
  250.   note : BOOLEAN;
  251.   URL  : INTEGER;
  252. BEGIN
  253.   res := GetRef(tag, note, URL)
  254.   RETURN note
  255. END IsNote;
  256.  
  257.  
  258. PROCEDURE CreateTag*(): TAG;
  259. VAR tag: TAG;
  260. BEGIN
  261.   NEW(tag);
  262.   tag.Visited := FALSE;
  263.   SU.MemError(tag = NIL);
  264.   INC(num);
  265.   tag.num := num;
  266.   V.push(Tags, tag)
  267.   RETURN tag
  268. END CreateTag;
  269.  
  270.  
  271. PROCEDURE CreateWord*(): WORD;
  272. VAR word: WORD;
  273. BEGIN
  274.   NEW(word);
  275.   SU.MemError(word = NIL)
  276.   RETURN word
  277. END CreateWord;
  278.  
  279.  
  280. PROCEDURE CreateSpace(): SPACE;
  281. VAR space: SPACE;
  282. BEGIN
  283.   NEW(space);
  284.   SU.MemError(space = NIL)
  285.   RETURN space
  286. END CreateSpace;
  287.  
  288.  
  289. PROCEDURE CreateAttr(): ATTR;
  290. VAR attr: ATTR;
  291. BEGIN
  292.   NEW(attr);
  293.   SU.MemError(attr = NIL)
  294.   RETURN attr
  295. END CreateAttr;
  296.  
  297.  
  298. PROCEDURE AddItem*(VAR list: LIST; item: ELEMENT);
  299. BEGIN
  300.   IF list.first = NIL THEN
  301.     list.first := item
  302.   ELSE
  303.     list.last.next := item
  304.   END;
  305.   list.last := item
  306. END AddItem;
  307.  
  308.  
  309. PROCEDURE DelLastItem*(VAR list: LIST);
  310. VAR cur: ELEMENT;
  311. BEGIN
  312.   IF list.first = list.last THEN
  313.     IF list.last # NIL THEN
  314.       DISPOSE(list.last)
  315.     END;
  316.     list.first := NIL
  317.   ELSE
  318.     cur := list.first;
  319.     WHILE cur.next # list.last DO
  320.       cur := cur.next
  321.     END;
  322.     DISPOSE(list.last);
  323.     cur.next := NIL;
  324.     list.last := cur
  325.   END
  326. END DelLastItem;
  327.  
  328.  
  329. PROCEDURE AddChild*(tag: TAG; child: ELEMENT);
  330. BEGIN
  331.   AddItem(tag.child, child);
  332.   child.parent := tag
  333. END AddChild;
  334.  
  335.  
  336. PROCEDURE AddAttr(tag: TAG; attr: ATTR);
  337. BEGIN
  338.   AddItem(tag.attr, attr);
  339.   attr.parent := tag
  340. END AddAttr;
  341.  
  342.  
  343. PROCEDURE Copy*(node: ELEMENT): ELEMENT;
  344. VAR
  345.   space : SPACE;
  346.   word  : WORD;
  347.   tag   : TAG;
  348.   cur   : ELEMENT;
  349.   num   : INTEGER;
  350.  
  351.   Result : ELEMENT;
  352. BEGIN
  353.   IF node IS TAG THEN
  354.     tag := CreateTag();
  355.     num := tag.num;
  356.     tag^ := node(TAG)^;
  357.     tag.num := num;
  358.     tag.child.first := NIL;
  359.     tag.child.last  := NIL;
  360.     cur := node(TAG).child.first;
  361.     WHILE cur # NIL DO
  362.       AddChild(tag, Copy(cur));
  363.       cur := cur.next
  364.     END;
  365.     Result := tag
  366.   ELSIF node IS WORD THEN
  367.     word := CreateWord();
  368.     word^ := node(WORD)^;
  369.     Result := word
  370.   ELSIF node IS SPACE THEN
  371.     space := CreateSpace();
  372.     space^ := node(SPACE)^;
  373.     Result := space
  374.   END;
  375.   Result.next := NIL
  376.   RETURN Result
  377. END Copy;
  378.  
  379.  
  380. PROCEDURE IsIdentChar(): BOOLEAN;
  381.   RETURN ("A" <= ch) & (ch <= "Z") OR
  382.          ("a" <= ch) & (ch <= "z") OR
  383.          ("0" <= ch) & (ch <= "9") OR
  384.          (ch  = "?") OR (ch  = "!") OR
  385.          (ch  = ":") OR (ch  = "_") OR
  386.          (ch  = "-")
  387. END IsIdentChar;
  388.  
  389.  
  390. PROCEDURE Space(): BOOLEAN;
  391.   RETURN (ch # 0X) & (ch <= 20X)
  392. END Space;
  393.  
  394.  
  395. PROCEDURE Ident(VAR id: S.CHARS);
  396. BEGIN
  397.   id.first := RF.Adr();
  398.   WHILE IsIdentChar() DO
  399.     RF.Next(ch)
  400.   END;
  401.   id.last := RF.Adr() - 1
  402. END Ident;
  403.  
  404.  
  405. PROCEDURE Skip;
  406. BEGIN
  407.   WHILE Space() DO
  408.     RF.Next(ch)
  409.   END
  410. END Skip;
  411.  
  412.  
  413. PROCEDURE String(VAR str: S.CHARS);
  414. VAR quot: CHAR;
  415. BEGIN
  416.   SU.ErrorIf((ch # "'") & (ch # 22X), 1);
  417.   str.first := RF.Adr();
  418.   quot := ch;
  419.   REPEAT
  420.     RF.Next(ch)
  421.   UNTIL (ch = quot) OR (ch = 0X);
  422.   SU.ErrorIf(ch = 0X, 2);
  423.   str.last := RF.Adr();
  424.   RF.Next(ch)
  425. END String;
  426.  
  427.  
  428. PROCEDURE SetTagValue(tag: TAG);
  429. VAR
  430.   value : INTEGER;
  431.   name  : S.CHARS;
  432. BEGIN
  433.   name := tag.name;
  434.   IF    S.CharsEqStr(name, "p") THEN
  435.     value := tag_p
  436.   ELSIF S.CharsEqStr(name, "v") THEN
  437.     value := tag_v
  438.   ELSIF S.CharsEqStr(name, "section") THEN
  439.     value := tag_section
  440.   ELSIF S.CharsEqStr(name, "stanza") THEN
  441.     value := tag_stanza
  442.   ELSIF S.CharsEqStr(name, "empty-line") THEN
  443.     value := tag_empty_line
  444.   ELSIF S.CharsEqStr(name, "subtitle") THEN
  445.     value := tag_subtitle
  446.   ELSIF S.CharsEqStr(name, "date") THEN
  447.     value := tag_date
  448.   ELSIF S.CharsEqStr(name, "text-author") THEN
  449.     value := tag_text_author
  450.   ELSIF S.CharsEqStr(name, "a") THEN
  451.     value := tag_a
  452.   ELSIF S.CharsEqStr(name, "sub") THEN
  453.     value := tag_sub
  454.   ELSIF S.CharsEqStr(name, "sup") THEN
  455.     value := tag_sup
  456.   ELSIF S.CharsEqStr(name, "code") THEN
  457.     value := tag_code
  458.   ELSIF S.CharsEqStr(name, "poem") THEN
  459.     value := tag_poem
  460.   ELSIF S.CharsEqStr(name, "title") THEN
  461.     value := tag_title
  462.   ELSIF S.CharsEqStr(name, "FictionBook") THEN
  463.     value := tag_FictionBook;
  464.     FB := tag
  465.   ELSIF S.CharsEqStr(name, "body") THEN
  466.     value := tag_body
  467.   ELSIF S.CharsEqStr(name, "strikethrough") THEN
  468.     value := tag_strikethrough
  469.   ELSIF S.CharsEqStr(name, "strong") THEN
  470.     value := tag_strong
  471.   ELSIF S.CharsEqStr(name, "cite") THEN
  472.     value := tag_cite
  473.   ELSIF S.CharsEqStr(name, "epigraph") THEN
  474.     value := tag_epigraph
  475.   ELSIF S.CharsEqStr(name, "emphasis") THEN
  476.     value := tag_emphasis
  477.   ELSIF S.CharsEqStr(name, "image") THEN
  478.     value := tag_image
  479.   ELSIF S.CharsEqStr(name, "binary") THEN
  480.     binary := TRUE;
  481.     value := tag_binary
  482.   ELSIF S.CharsEqStr(name, "coverpage") THEN
  483.     value := tag_coverpage
  484.   ELSIF S.CharsEqStr(name, "description") THEN
  485.     value := tag_description
  486.   ELSIF S.CharsEqStr(name, "annotation") THEN
  487.     value := tag_annotation
  488.   ELSIF S.CharsEqStr(name, "table") THEN
  489.     value := tag_table
  490.   ELSIF S.CharsEqStr(name, "tr") THEN
  491.     value := tag_tr
  492.   ELSIF S.CharsEqStr(name, "td") THEN
  493.     value := tag_td
  494.   ELSIF S.CharsEqStr(name, "th") THEN
  495.     value := tag_th
  496.   ELSIF S.CharsEqStr(name, "?xml") THEN
  497.     value := tag_xml;
  498.     Header := tag
  499.   ELSE
  500.     value := tag_unknown
  501.   END;
  502.   tag.value := value
  503. END SetTagValue;
  504.  
  505.  
  506. PROCEDURE ReadTag;
  507. VAR tag: TAG; name: S.CHARS; attr: ATTR; tag_id: TAG_ID;
  508. BEGIN
  509.   RF.Next(ch);
  510.   Skip;
  511.   IF ch = "/" THEN
  512.     RF.Next(ch);
  513.     Skip;
  514.     SU.ErrorIf(~IsIdentChar(), 3);
  515.     Ident(name);
  516.     Skip;
  517.     SU.ErrorIf(ch # ">", 4);
  518.     RF.Next(ch);
  519.     tag := Current(TAG);
  520.     SU.ErrorIf(~S.CharsEq(tag.name, name), 5);
  521.     IF tag.value = tag_binary THEN
  522.       binary := FALSE;
  523.       IF tag.child.first IS WORD THEN
  524.         S.Base64(tag.child.first(WORD).value)
  525.       END
  526.     END;
  527.     Current := Current.parent
  528.   ELSE
  529.     tag := CreateTag();
  530.     AddChild(Current(TAG), tag);
  531.     Current := tag;
  532.     SU.ErrorIf(~IsIdentChar(), 6);
  533.     Ident(tag.name);
  534.     SetTagValue(tag);
  535.     WHILE Space() DO
  536.       Skip;
  537.       IF IsIdentChar() THEN
  538.         attr := CreateAttr();
  539.         Ident(attr.name);
  540.         Skip;
  541.         SU.ErrorIf(ch # "=", 7);
  542.         RF.Next(ch);
  543.         Skip;
  544.         String(attr.value);
  545.         AddAttr(Current(TAG), attr);
  546.         IF S.CharsEqStr(attr.name, "id") THEN
  547.           NEW(tag_id);
  548.           SU.MemError(tag_id = NIL);
  549.           tag_id.tag := Current(TAG);
  550.           tag_id.id  := attr.value;
  551.           INC(tag_id.id.first);
  552.           DEC(tag_id.id.last);
  553.           AddItem(Tag_id, tag_id)
  554.         END
  555.       END
  556.     END;
  557.     IF ch = "/" THEN
  558.       RF.Next(ch);
  559.       IF Current(TAG).value = tag_binary THEN
  560.         binary := FALSE
  561.       END;
  562.       Current := Current.parent
  563.     ELSIF ch = "?" THEN
  564.       RF.Next(ch);
  565.       SU.ErrorIf(Current(TAG).value # tag_xml, 8);
  566.       Current := Current.parent
  567.     END;
  568.     SU.ErrorIf(ch # ">", 9);
  569.     RF.Next(ch)
  570.   END
  571. END ReadTag;
  572.  
  573.  
  574. PROCEDURE ReadSpace;
  575. VAR space: SPACE;
  576. BEGIN
  577.   space := CreateSpace();
  578.   AddChild(Current(TAG), space);
  579.   RF.Next(ch)
  580. END ReadSpace;
  581.  
  582.  
  583. PROCEDURE ReadWord;
  584. VAR word: WORD; chars: S.CHARS; repl: BOOLEAN;
  585. BEGIN
  586.   word := CreateWord();
  587.   word.value.first := RF.Adr();
  588.   repl := FALSE;
  589.   WHILE ((ch > 20X) OR binary) & (ch # 0X) & (ch # "<") DO
  590.     repl := repl OR (ch = "&") OR (ch = 0C2X) OR (ch >= 0E0X) & (ch < 0F0X);
  591.     RF.Next(ch)
  592.   END;
  593.   word.value.last := RF.Adr() - 1;
  594.   IF repl THEN
  595.     chars := word.value;
  596.     S.Replace(chars, "&amp;",  "&");
  597.     S.Replace(chars, "&lt;",   "<");
  598.     S.Replace(chars, "&gt;",   ">");
  599.     S.Replace(chars, "&quot;", 22X);
  600.     S.Replace(chars, "&apos;", "'");
  601.     WHILE S.EntOct(chars) DO END;
  602.     S.Replace(chars, tire1, "--");
  603.     S.Replace(chars, tire2, "--");
  604.     S.Replace(chars, nbsp, " ");
  605.     S.Replace(chars, ellipsis, "...");
  606.     S.Replace(chars, quot1, 22X);
  607.     S.Replace(chars, quot2, 22X);
  608.     S.Replace(chars, quot3, 22X);
  609.     S.Replace(chars, quot4, "'");
  610.     S.Replace(chars, quot5, ",");
  611.     S.Replace(chars, quot6, "<");
  612.     S.Replace(chars, quot7, ">");
  613.     S.Replace(chars, number, "No.");
  614.     S.Replace(chars, apo, "'");
  615.     S.Replace(chars, dash1, "-");
  616.     S.Replace(chars, dash2, "-");
  617.     S.Replace(chars, bullet, "*");
  618.     S.Replace(chars, euro, "EUR");
  619.     word.value := chars
  620.   END;
  621.   AddChild(Current(TAG), word)
  622. END ReadWord;
  623.  
  624.  
  625. PROCEDURE Comment(): BOOLEAN;
  626. CONST com = 2D2D213CH;
  627. VAR res: BOOLEAN;
  628. BEGIN
  629.   res := FALSE;
  630.   IF RF.Int() = com THEN
  631.     RF.Next(ch);
  632.     RF.Next(ch);
  633.     RF.Next(ch);
  634.     RF.Next(ch);
  635.  
  636.     REPEAT
  637.       RF.Next(ch);
  638.       IF ch = "-" THEN
  639.         RF.Next(ch);
  640.         WHILE (ch = "-") & ~res DO
  641.           RF.Next(ch);
  642.           IF ch = ">" THEN
  643.             RF.Next(ch);
  644.             res := TRUE
  645.           END
  646.         END
  647.       END
  648.     UNTIL (ch = 0X) OR res
  649.  
  650.   END
  651.   RETURN res
  652. END Comment;
  653.  
  654.  
  655. PROCEDURE Prolog;
  656. VAR attr: ATTR; chars: S.CHARS;
  657. BEGIN
  658.   RF.Next(ch);
  659.   IF ch = 0EFX THEN
  660.     RF.Next(ch);
  661.     SU.ErrorIf(ch # 0BBX, 16);
  662.     RF.Next(ch);
  663.     SU.ErrorIf(ch # 0BFX, 16);
  664.     RF.Next(ch)
  665.   END;
  666.   Skip;
  667.   IF ch = "<" THEN
  668.     ReadTag
  669.   END;
  670.  
  671.   SU.ErrorIf(Header = NIL, 15);
  672.  
  673.   attr := Header(TAG).attr.first(ATTR);
  674.   WHILE attr # NIL DO
  675.     IF S.CharsEqStr(attr.name, "encoding") THEN
  676.       chars := attr.value;
  677.       INC(chars.first);
  678.       DEC(chars.last);
  679.       S.SetCS(FALSE);
  680.       IF    S.CharsEqStr(chars, "windows-1250") THEN
  681.         RF.Conv(Encode.W1250)
  682.       ELSIF S.CharsEqStr(chars, "windows-1251") THEN
  683.         RF.Conv(Encode.W1251)
  684.       ELSIF S.CharsEqStr(chars, "windows-1252") THEN
  685.         RF.Conv(Encode.W1252)
  686.       ELSIF S.CharsEqStr(chars, "cp866"       ) THEN
  687.         RF.Conv(Encode.CP866)
  688.       ELSIF S.CharsEqStr(chars, "utf-8"       ) THEN
  689.         RF.SeekBeg
  690.       ELSE
  691.         SU.ErrorIf(TRUE, 14)
  692.       END;
  693.       S.SetCS(TRUE)
  694.     END;
  695.     attr := attr.next(ATTR)
  696.   END
  697. END Prolog;
  698.  
  699.  
  700. PROCEDURE Parse;
  701. BEGIN
  702.   Prolog;
  703.   binary := FALSE;
  704.   RF.Next(ch);
  705.   WHILE ch = "<" DO
  706.     IF ~Comment() THEN
  707.       ReadTag
  708.     END
  709.   ELSIF Space() & ~binary DO
  710.     ReadSpace
  711.   ELSIF (ch # 0X) DO
  712.     ReadWord
  713.   END
  714. END Parse;
  715.  
  716.  
  717. PROCEDURE Open*(FileName: S.STRING);
  718. BEGIN
  719.   Root := CreateTag();
  720.   Current := Root;
  721.   Header := NIL;
  722.   FB := NIL;
  723.   num := 0;
  724.   RF.Load(FileName);
  725.   Parse;
  726.   SU.ErrorIf(Current # Root, 10)
  727. END Open;
  728.  
  729.  
  730. PROCEDURE Init;
  731. BEGIN
  732.   S.utf8(8212, tire1);
  733.   S.utf8(8211, tire2);
  734.   S.utf8( 160, nbsp);
  735.   S.utf8(8230, ellipsis);
  736.   S.utf8(8217, apo);
  737.   S.utf8(8220, quot1);
  738.   S.utf8(8221, quot2);
  739.   S.utf8(8222, quot3);
  740.   S.utf8(8216, quot4);
  741.   S.utf8(8218, quot5);
  742.   S.utf8(8249, quot6);
  743.   S.utf8(8250, quot7);
  744.   S.utf8(8470, number);
  745.   S.utf8(8208, dash1);
  746.   S.utf8(8209, dash2);
  747.   S.utf8(8226, bullet);
  748.   S.utf8(8364, euro);
  749.   Tags := V.create(1024)
  750. END Init;
  751.  
  752.  
  753. BEGIN
  754.   Init
  755. END XML.
  756.