Subversion Repositories Kolibri OS

Rev

Rev 9896 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
9896 akron1 1
(*
9898 akron1 2
    Copyright 2016, 2020, 2022, 2023 Anton Krotov
9896 akron1 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 .
18
*)
19
 
20
MODULE XML;
21
 
9898 akron1 22
IMPORT SU := SysUtils, RF := ReadFile, S := Strings, E := Encoding, V := Vector, tables, LISTS;
9896 akron1 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,
9898 akron1 138
    dash1, dash2: E.tUtf8;
9896 akron1 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, "&",  "&");
597
    S.Replace(chars, "<",   "<");
598
    S.Replace(chars, ">",   ">");
599
    S.Replace(chars, """, 22X);
600
    S.Replace(chars, "'", "'");
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
9898 akron1 681
        RF.Conv(E.cp1250)
9896 akron1 682
      ELSIF S.CharsEqStr(chars, "windows-1251") THEN
9898 akron1 683
        RF.Conv(E.cp1251)
9896 akron1 684
      ELSIF S.CharsEqStr(chars, "windows-1252") THEN
9898 akron1 685
        RF.Conv(E.cp1252)
9896 akron1 686
      ELSIF S.CharsEqStr(chars, "cp866"       ) THEN
9898 akron1 687
        RF.Conv(E.cp866)
9896 akron1 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
9898 akron1 732
  E.utf8(8212, tire1);
733
  E.utf8(8211, tire2);
734
  E.utf8( 160, nbsp);
735
  E.utf8(8230, ellipsis);
736
  E.utf8(8217, apo);
737
  E.utf8(8220, quot1);
738
  E.utf8(8221, quot2);
739
  E.utf8(8222, quot3);
740
  E.utf8(8216, quot4);
741
  E.utf8(8218, quot5);
742
  E.utf8(8249, quot6);
743
  E.utf8(8250, quot7);
744
  E.utf8(8470, number);
745
  E.utf8(8208, dash1);
746
  E.utf8(8209, dash2);
747
  E.utf8(8226, bullet);
748
  E.utf8(8364, euro);
9896 akron1 749
  Tags := V.create(1024)
750
END Init;
751
 
752
 
753
BEGIN
754
  Init
755
END XML.