source: tags/parabix-0.39/src/engine.c @ 4027

Last change on this file since 4027 was 30, checked in by cameron, 12 years ago

Unify DQuote/SQuote stream; xmlns bugfix

File size: 18.0 KB
Line 
1/*  engine.c - Parabix XML parsing engine.
2    Copyright (c) 2007, 2008, Robert D. Cameron.
3    Licensed to the public under the Open Software License 3.0.
4    Licensed to International Characters, Inc., under the Academic
5    Free License 3.0.
6*/
7
8#include "ilax.h"
9#include "engine.h"
10#include "xmlbuffer.h"
11#include "bytelex.h"
12#include "bitlex.h"
13#include "charsets/ext_ascii_8.h"
14#include "charsets/ext_ascii_16.h"
15
16#include <assert.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20
21
22template <CodeUnit_Base C>
23ParsingEngine<C>::ParsingEngine (char * filename) {
24#ifdef BUFFER_PROFILING
25        bitstream_timer = init_BOM_timer(BUFFER_BLOCKS * BLOCKSIZE);
26        lextranspose_timer = init_BOM_timer(BUFFER_BLOCKS * BLOCKSIZE);
27        scanner_timer = init_BOM_timer(BUFFER_BLOCKS * BLOCKSIZE);
28#endif
29
30#define OFFSET32_SENTINEL
31
32  /* Install sentinels for every lexical item stream*/
33#ifndef OPTIMIZE_SHORT_SCAN
34  BitBlock sentinel_value = simd_const_1(1);
35#endif
36#ifdef OPTIMIZE_SHORT_SCAN
37  BitBlock sentinel_value = sisd_sfli(simd_const_1(1), 8*sizeof(unsigned long));
38#endif
39  for (int j = MarkupStart; j < LexicalItemCount; j++) {
40    buf.item_stream[j][BUFFER_BLOCKS] = sentinel_value;
41  }
42  buffer_base_pos = 0;
43  buffer_rel_pos = 0;
44  xml_buf = new XML_Buffer::XML_Buffer(filename, BLOCKSIZE);
45}
46
47
48template <CodeUnit_Base C>
49void ParsingEngine<C>::InitLexer() {
50  unsigned char * sentinel = (unsigned char *) "<![--?>]]>/'>\"><!";
51  if (xml_buf->PrepareBytes(0, 4) < 4) {
52    printf("No XML input document.\n");
53    exit(-1);
54  }
55  unsigned char * XML_signature = xml_buf->BytePtr(0);
56  xml_buf->InstallPadding(sentinel);
57
58  Charset_Family family = Charset_Family_Detect(XML_signature);
59  switch (family) {
60    case Ext_ASCII_8:
61      printf("Ext_ASCII_8 document detected.\n");
62      lex = new Ext_ASCII_8_Lexer::Ext_ASCII_8_Lexer(xml_buf, &buf);
63      //lex = new Lexer::Lexer(xml_buf, &buf);
64      //xml_buf->InstallPadding(sentinel);
65      break;
66    case Ext_ASCII_16BE:
67      printf("Ext_ASCII_16BE document detected.\n");
68      lex = new Ext_ASCII_16BE_Lexer::Ext_ASCII_16BE_Lexer(xml_buf, &buf);
69      break;
70    case Ext_ASCII_16LE:
71      printf("Ext_ASCII_16LE document detected.\n");
72      lex = new Ext_ASCII_16LE_Lexer::Ext_ASCII_16LE_Lexer(xml_buf, &buf);
73      break;
74    default:
75      printf("Error: Charset family %i detected, but not supported.\n", family);
76      exit(-1);
77  }
78  avail_code_units = lex-> AdvanceBuffer(0);
79}
80
81
82
83template <CodeUnit_Base C>
84inline void ParsingEngine<C>::AdvanceToNewBasePosn(int advance_amt) {
85  buffer_base_pos += advance_amt;
86  buffer_rel_pos = 0;
87}
88
89template <CodeUnit_Base C>
90inline unsigned char * ParsingEngine<C>::cur() const {
91  return &((unsigned char *) buf.x8data)[buffer_rel_pos];
92}
93
94template <CodeUnit_Base C>
95inline int ParsingEngine<C>::AbsPos() const {
96  return buffer_base_pos + buffer_rel_pos;
97}
98
99
100template <CodeUnit_Base C>
101inline int ParsingEngine<C>::BufferRelPos() const {
102  return buffer_rel_pos;
103}
104
105
106template <CodeUnit_Base C>
107inline bool ParsingEngine<C>::at_EOF() const {
108  return (buffer_rel_pos >= avail_code_units) && 
109         (avail_code_units < BUFFER_BLOCKS * BLOCKSIZE + LOOKAHEAD_POSITIONS);
110}
111
112template <CodeUnit_Base C>
113inline void ParsingEngine<C>::Advance(int n) {
114  buffer_rel_pos += n;
115#ifndef OMIT_BITBUFFER_LIMIT_TEST_IN_ADVANCE
116  if (buffer_rel_pos >= BUFFER_BLOCKS * BLOCKSIZE) {
117#ifdef BUFFER_PROFILING
118    end_BOM_interval(scanner_timer);
119#endif
120    AdvanceToNewBasePosn(buffer_rel_pos);
121    avail_code_units = lex->AdvanceBuffer(AbsPos());
122  }
123#endif
124}
125
126template <CodeUnit_Base C>
127inline void ParsingEngine<C>::ASCII_ScanTo(int item) {
128#ifdef DEBUG_BYTESCAN
129  int p1 = AbsPos();
130#endif
131  switch (item) {
132    case NonWS: while (at_WhiteSpace<XML_1_0, ASCII>(cur())) Advance(1); break;
133    case MarkupStart: while(!AtChar<ASCII,'<'>(cur()) && !AtChar<ASCII,'&'>(cur()) && !at_CDATA_End<ASCII>(cur())) Advance(1); break;
134    case CD_End_check: while(!at_CDATA_End<ASCII>(cur())) Advance(1); break;
135    case Hyphen: while(!AtChar<ASCII,'-'>(cur())) Advance(1); break;
136    case QMark: while(!AtChar<ASCII,'?'>(cur())) Advance(1); break;
137    case Quote: while(!AtChar<ASCII,'<'>(cur()) && !AtChar<ASCII,'&'>(cur()) && !AtQuote<ASCII>(cur())) Advance(1); break;
138    case NameFollow: while(!at_WhiteSpace<XML_1_0, ASCII>(cur()) && !AtChar<ASCII,';'>(cur()) && !AtChar<ASCII,'/'>(cur()) && !AtChar<ASCII,'>'>(cur())
139                      && !AtChar<ASCII,'='>(cur()) && !AtChar<ASCII,'?'>(cur())) Advance(1); break;
140  }
141#ifdef DEBUG_BYTESCAN
142  printf("ASCII_ScanTo(%i) %i -> %i\n", item, p1, AbsPos());
143#endif
144}
145
146
147#ifndef OPTIMIZE_SHORT_SCAN
148template <CodeUnit_Base C>
149inline void ParsingEngine<C>::ScanTo(int item) {
150  buffer_rel_pos = bitstream_scan(buf.item_stream[item], 
151                                      buffer_rel_pos);
152  while (buffer_rel_pos >= BUFFER_BLOCKS * BLOCKSIZE) {
153#ifdef BUFFER_PROFILING
154    end_BOM_interval(scanner_timer);
155#endif
156    AdvanceToNewBasePosn(buffer_rel_pos);
157    avail_code_units = lex->AdvanceBuffer(AbsPos());
158    buffer_rel_pos = bitstream_scan0(buf.item_stream[item]);
159  }
160}
161#endif
162
163#ifdef OPTIMIZE_SHORT_SCAN
164template <CodeUnit_Base C>
165inline void ParsingEngine<C>::ScanTo(int item) {
166  SIMD_type * stream = buf.item_stream[item];
167  unsigned long * bitstream_ptr = (unsigned long *) (((intptr_t) stream) + buffer_rel_pos/8);
168  unsigned long bitstream_slice = *bitstream_ptr >> buffer_rel_pos % 8;
169  if (bitstream_slice != 0) {
170    buffer_rel_pos += __builtin_ctzl(bitstream_slice);
171  }
172  else {
173    buffer_rel_pos = (buffer_rel_pos & -8) + 8*sizeof(unsigned long);
174    buffer_rel_pos += bitstream_scan0((SIMD_type *) &bitstream_ptr[1]);
175    while (buffer_rel_pos >= BUFFER_BLOCKS * BLOCKSIZE) {
176      buffer_rel_pos = BUFFER_BLOCKS * BLOCKSIZE;
177#ifdef BUFFER_PROFILING
178      end_BOM_interval(scanner_timer);
179#endif
180      AdvanceToNewBasePosn(buffer_rel_pos);
181      avail_code_units = lex->AdvanceBuffer(AbsPos());
182      buffer_rel_pos = bitstream_scan0(buf.item_stream[item]);
183    }
184  }
185}
186#endif
187
188/* Parse a markup item beginning '<' */
189template <CodeUnit_Base C>
190inline void ParsingEngine<C>::Parse_Markup() {
191        int markup_start = AbsPos();
192        if (at_ElementTag_Start<C>(cur())) {
193                Parse_StartTag();
194        }
195        else if (at_EndTag_Start<C>(cur())) {
196                Parse_EndTag();
197        }
198        else if (at_Comment_Start<C>(cur())) {
199                Parse_Comment();
200        }
201        else if (at_CDATA_Start<C>(cur())) {
202                Parse_CDATA();
203        }
204        else if (at_PI_Start<C>(cur())) {
205                Parse_PI();
206        }
207        else {
208                Advance(1);
209                Error_action(markup_start, AbsPos());
210        }
211}
212
213/* Parse a comment beginning "<!--" */
214template <CodeUnit_Base C>
215inline void ParsingEngine<C>::Parse_Comment() {
216        int markup_start = AbsPos();
217        Advance(4); /* Skip "<!--". */
218        ScanTo(Hyphen);
219        while (!at_DoubleHyphen<C>(cur())) {
220                Advance(2); /* Skip hyphen-nonhyphen pair */
221                ScanTo(Hyphen); 
222        }
223        if (at_Comment_End<C>(cur())) {
224                Advance(3); /* Skip "-->". */
225                Comment_action(markup_start, AbsPos());
226        }
227        else {
228                Advance(2);  /* "--" */
229                Error_action(markup_start, AbsPos());
230        }
231}
232
233/* Parse an end tag beginning "</" */
234template <CodeUnit_Base C>
235inline void ParsingEngine<C>::Parse_EndTag() {
236        int markup_start = AbsPos();
237#ifndef OMIT_ADVANCE_PRIOR_TO_EXCLUSIVE_SCAN
238        Advance(2); /* Skip "</". */
239#endif
240        ScanTo(NameFollow);
241        if (AtChar<C,'>'>(cur())) {
242                Advance(1);
243                EndTag_action(markup_start, AbsPos());
244        }
245        else {
246                ScanTo(NonWS);
247                if (AtChar<C,'>'>(cur())) {
248                        Advance(1);
249                        EndTag_action(markup_start, AbsPos());
250                }
251                else Error_action(markup_start, AbsPos());
252        }
253}
254
255/* Parse a CDATA section beginning "<![CDATA". */
256template <CodeUnit_Base C>
257inline void ParsingEngine<C>::Parse_CDATA() {
258        int markup_start = AbsPos();
259        Advance(8); /* Skip "<![CDATA". */
260        if (!AtChar<C,'['>(cur())) {
261                Error_action(markup_start, AbsPos());
262        }
263        else {
264                ScanTo(CD_End_check);
265                while (!at_CDATA_End<C>(cur())) {
266                        Advance(1);
267                        ScanTo(CD_End_check);
268                }
269                Advance(3); /* Skip "]]>". */
270                CDATA_action(markup_start, AbsPos());
271        }
272}
273
274template <CodeUnit_Base C>
275inline void ParsingEngine<C>::Parse_Reference() {
276        int markup_start = AbsPos();
277        /* Advance(1);  // skip "&" */
278        ScanTo(NameFollow);  /* Name delimiter */
279        if (!AtChar<C,';'>(cur())) {
280                Error_action(markup_start, AbsPos());
281        }
282        else {
283                Advance(1);
284                Reference_action(markup_start, AbsPos());
285        }
286}
287
288template <CodeUnit_Base C>
289inline void ParsingEngine<C>::Parse_PI () {
290        int markup_start = AbsPos();
291        Advance(2); /* Skip "<?". */
292        int target_start = AbsPos();
293        // Check for illegal [Xx][Mm][Ll] target.
294        if (at_XxMmLll_WS<C>(cur())) {
295                Advance(4);
296                Error_action(markup_start, AbsPos());
297                return;
298        } 
299        ScanTo(NameFollow);  /* Name delimiter */
300        PI_Target_action(target_start, AbsPos());
301        ScanTo(QMark);
302        while (!at_PI_End<C>(cur())) {
303                Advance(1);
304                ScanTo(QMark);
305        }
306        Advance(2); /* Skip "?>". */
307        PI_action(markup_start, AbsPos());
308}
309 
310/* Parse a start or empty element tag. */
311template <CodeUnit_Base C>
312inline void ParsingEngine<C>::Parse_StartTag () {
313        int markup_start = AbsPos();
314        int att_name_start;
315        int att_val_start;
316        int att_name_end, att_val_end;
317        unsigned char quoteCh;
318        ScanTo(NameFollow);  /* Name delimiter: WS, "/" or ">" */
319        ElementName_action(markup_start+1, AbsPos());
320        /* The following test optimizes the most common case of a
321        start tag with no attributes.  */
322        if (AtChar<C,'>'>(cur())) {
323                Advance(1);
324                StartTag_action(markup_start, AbsPos());
325        }
326        else {
327                ScanTo(NonWS);
328                if (AtChar<C,'>'>(cur())) {
329                        Advance(1);
330                        StartTag_action(markup_start, AbsPos());
331                }
332                else if (at_EmptyElementDelim<C>(cur())) {
333                        Advance(2);
334                        EmptyElement_action(markup_start, AbsPos());
335                }
336                else do {
337                        /* Must be an attribute-value pair or error. */
338                        att_name_start = AbsPos();
339                        ScanTo(NameFollow);
340                        att_name_end = AbsPos();
341                        /* The following optimized tests handle the frequently occurring
342                        case that there are no blanks on either side of the equals sign.
343                        In many cases, the very first test handles 100% of actual
344                        attribute-value pairs encountered. */
345                        if (at_EqualsQuote<C>(cur())) {
346                                quoteCh = cur()[1];
347                                Advance(2); 
348                        }
349                        else {
350                                ScanTo(NonWS);
351                                if (!AtChar<C,'='>(cur())) {
352                                        Error_action(markup_start, AbsPos()); 
353                                        break;
354                                }
355                                ScanTo(NonWS);
356                                quoteCh = cur()[0];
357                                if (!AtQuote<C>(cur())) {
358                                        Error_action(markup_start, AbsPos()); 
359                                        break;
360                                }
361                                Advance(1);
362                        }
363                        att_val_start = AbsPos();
364                        ScanTo(Quote);
365                        while (cur()[0] != quoteCh) {
366                                if (AtChar<C,'&'>(cur())) {
367                                        Parse_Reference();
368                                        ScanTo(Quote);
369                                }
370                                else if (AtQuote<C>(cur())) {
371                                        Advance(1);
372                                }
373                                else /* if (AtChar<C,'<'>(cur())) */{
374                                        Error_action(markup_start, AbsPos()); 
375                                        break;
376                                }
377                        }
378                        att_val_end = AbsPos();
379                        Advance(1); 
380                        if (at_xmlns<C>(cur()+att_name_start-AbsPos())) {
381                                Namespace_action(att_name_start, att_name_end,
382                                                 att_val_start, att_val_end);
383                        }
384                        else {
385                                AttributeValue_action(att_name_start, att_name_end,
386                                                      att_val_start, att_val_end);
387                        }
388                        /* Now check for end or repeat. Avoid whitespace scan if possible.*/
389                        if (AtChar<C,'>'>(cur())) {
390                                Advance(1);
391                                StartTag_action(markup_start, AbsPos());
392                                break;
393                        }
394                        else if (at_EmptyElementDelim<C>(cur())) {
395                                Advance(2);
396                                EmptyElement_action(markup_start, AbsPos());
397                                break;
398                        }
399                        ScanTo(NonWS);
400                        if (AtChar<C,'>'>(cur())) {
401                                Advance(1);
402                                StartTag_action(markup_start, AbsPos());
403                                break;
404                        }
405                        else if (at_EmptyElementDelim<C>(cur())) {
406                                Advance(2);
407                                EmptyElement_action(markup_start, AbsPos());
408                                break;
409                        }
410                        else if (AbsPos() == att_val_end + 1) { 
411                                /* No WS following att value */
412                                Error_action(markup_start, AbsPos());
413                                break;
414                        }
415                } while (1);
416        }
417}
418
419
420template <CodeUnit_Base C>
421inline void ParsingEngine<C>::ParseContent () {
422        int text_start = AbsPos();
423        do {
424                ScanTo(MarkupStart); /* '<', '&', or ']' for ']]>' test */
425/*              if (AtChar<C,'<'>(cur())) {
426                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
427                        Parse_Markup<C>();
428                }*/
429                int markup_start = AbsPos();
430                if (at_ElementTag_Start<C>(cur())) {
431                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
432                        Parse_StartTag();
433                }
434                else if (at_EndTag_Start<C>(cur())) {
435                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
436                        Parse_EndTag();
437                }
438                else if (at_Comment_Start<C>(cur())) {
439                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
440                        Parse_Comment();
441                }
442                else if (AtChar<C,'&'>(cur())) {
443                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
444                        Parse_Reference();
445                }
446                else if (at_CDATA_Start<C>(cur())) {
447                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
448                        Parse_CDATA();
449                }
450                else if (at_PI_Start<C>(cur())) {
451                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
452                        Parse_PI();
453                }
454                else if (at_CDATA_End<C>(cur())) {
455                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
456                        Advance(3);
457                        Error_action(AbsPos()-3, AbsPos());
458                }
459                else if (at_EOF()) {
460                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
461                        break;
462                }
463                else {
464                        Advance(1);
465                        continue;
466                }
467                text_start = AbsPos();
468        } while (1);
469#ifdef BUFFER_PROFILING
470        printf("Bit stream computation.\n");
471        dump_BOM_table(bitstream_timer);
472        printf("Lexical stream transposition.\n");
473        dump_BOM_table(lextranspose_timer);
474        printf("Scanning.\n");
475        dump_BOM_table(scanner_timer);
476#endif
477}
478
479//
480// The following does not yet validate the syntax of EncNames.
481// EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
482// Future approach: first use lookup in EncNameTable,
483//           if not found, do case convert, try again,
484//             (avoids cost of case convert normally)
485//           if not found, validate syntax of EncNames,
486//           report error or EncName unknown.
487//
488template <CodeUnit_Base C>
489void ParsingEngine<C>::ReadXmlInfo(Entity_Declaration_Info& xml_info) {
490  int BOM = lex->BOM_size(0);
491  xml_info.has_ByteOrderMark = BOM > 0;
492  xml_info.has_version_decl = false;
493  xml_info.has_encoding_decl = false;
494  xml_info.has_standalone_decl = false;
495  Advance(BOM);
496  int decl_start = AbsPos();
497  // It is possible that there is no XML declaration.
498  if (!at_XmlDecl_start<C>(cur())) return;
499  // Otherwise, the XML declaration exists and must have
500  // at least version information.
501  xml_info.has_version_decl = true;
502  Advance(6);
503  ASCII_ScanTo(NonWS);
504  if (!at_version<C>(cur())) {Error_action(decl_start, AbsPos()); return;}
505  Advance(7);
506  ASCII_ScanTo(NonWS);
507  if (!AtChar<C,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
508  Advance(1);
509  ASCII_ScanTo(NonWS);
510  if (at_1_0<C>(cur())) xml_info.version = 0;
511  else if (at_1_1<C>(cur())) xml_info.version = 1;
512  else {Error_action(decl_start, AbsPos()); return;}
513  Advance(5);
514  if (at_PI_End<C>(cur())) {Advance(2); return;}
515  if (!at_WhiteSpace<XML_1_0, C>(cur())) {Error_action(decl_start, AbsPos()); return
516;}
517  ASCII_ScanTo(NonWS);
518  if (at_encoding<C>(cur())) {
519      xml_info.has_encoding_decl = true;
520      Advance(8);
521      ASCII_ScanTo(NonWS);
522      if (!AtChar<C,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
523      Advance(1);
524      ASCII_ScanTo(NonWS);
525      xml_info.encoding_start_pos = AbsPos()+1;
526      if (AtQuote<C>(cur())) {
527        unsigned char quoteCh = cur()[0];
528        Advance(1);
529        ASCII_ScanTo(Quote);
530        if (cur()[0] != quoteCh) {Error_action(decl_start, AbsPos()); return;}
531      }
532      else {Error_action(decl_start, AbsPos()); return;}
533      xml_info.encoding_end_pos = AbsPos();
534      Advance(1);
535      if (at_PI_End<C>(cur())) {Advance(2); return;}
536      if (!at_WhiteSpace<XML_1_0, C>(cur())) 
537                {Error_action(decl_start, AbsPos()); return;}
538      ASCII_ScanTo(NonWS);
539  }
540  if (at_standalone<C>(cur())) {
541      xml_info.has_standalone_decl = true;
542      Advance(10);
543      ASCII_ScanTo(NonWS);
544      if (!AtChar<C,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
545      Advance(1);
546      ASCII_ScanTo(NonWS);
547      if (at_yes<C>(cur())) {Advance(5); xml_info.standalone = true;}
548      else if (at_no<C>(cur())) {Advance(4); xml_info.standalone = false;}
549      else {Error_action(decl_start, AbsPos()); return;}
550      ASCII_ScanTo(NonWS);
551  }
552  if (at_PI_End<C>(cur())) {Advance(2); return;}
553  else {Error_action(decl_start, AbsPos()); return;}
554}
555
556// Similar to reading the XML_declaration of the document entity,
557// ReadTextDeclaration reads the text declaration of an external
558// parsed entity.  This is not really needed at present, for DTDless
559// processing.
560template <CodeUnit_Base C>
561void ParsingEngine<C>::ReadTextDeclaration(Entity_Declaration_Info& xml_info) {
562  int BOM = lex->BOM_size(0);
563  xml_info.has_ByteOrderMark = BOM > 0;
564  xml_info.has_version_decl = false;
565  xml_info.has_encoding_decl = false;
566  xml_info.has_standalone_decl = false;
567  Advance(BOM);
568  int decl_start = AbsPos();
569  // It is possible that there is no XML declaration.
570  if (!at_XmlDecl_start<C>(cur())) return;
571  // Otherwise, the text declaration exists and may have
572  // version information.
573  Advance(6);
574  ASCII_ScanTo(NonWS);
575  if (at_version<C>(cur())) {
576    xml_info.has_version_decl = true;
577    Advance(7);
578    ASCII_ScanTo(NonWS);
579    if (!AtChar<C,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
580    Advance(1);
581    ASCII_ScanTo(NonWS);
582    if (at_1_0<C>(cur())) xml_info.version = 0;
583    else if (at_1_1<C>(cur())) xml_info.version = 1;
584    else {Error_action(decl_start, AbsPos()); return;}
585    Advance(5);
586    // Must have whitespace character before declaration.
587    if (!at_WhiteSpace<XML_1_0, C>(cur())) 
588        {Error_action(decl_start, AbsPos()); return;}
589    ASCII_ScanTo(NonWS);
590  }
591  if (!at_encoding<C>(cur())) {Error_action(decl_start, AbsPos()); return;}
592  xml_info.has_encoding_decl = true;
593  Advance(8);
594  ASCII_ScanTo(NonWS);
595  if (!AtChar<C,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
596  Advance(1);
597  ASCII_ScanTo(NonWS);
598  xml_info.encoding_start_pos = AbsPos()+1;
599  if (AtQuote<C>(cur())) {
600        unsigned char quoteCh = cur()[0];
601        Advance(1);
602        ASCII_ScanTo(Quote);
603        if (cur()[0] != quoteCh) {Error_action(decl_start, AbsPos()); return;}
604  }
605  else {Error_action(decl_start, AbsPos()); return;}
606  xml_info.encoding_end_pos = AbsPos();
607  Advance(1);
608  ASCII_ScanTo(NonWS);
609  if (at_PI_End<C>(cur())) {Advance(2); return;}
610  else {Error_action(decl_start, AbsPos()); return;}
611}
Note: See TracBrowser for help on using the repository browser.