SgmlParser.cs
上传用户:jingke1993
上传日期:2022-06-08
资源大小:140k
文件大小:73k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. using System;
  2. using System.IO;
  3. using System.Collections;
  4. using System.Text;
  5. using System.Net;
  6. using System.Xml;
  7. using System.Globalization;
  8. namespace Sgml {
  9.     public enum LiteralType {
  10.         CDATA, SDATA, PI
  11.     };
  12.     public class Entity {
  13.         public const Char EOF = (char)65535;
  14.         public string Proxy;
  15.         public string Name;
  16.         public bool Internal;
  17.         public string PublicId;
  18.         public string Uri;
  19.         public string Literal;
  20.         public LiteralType LiteralType;
  21.         public Entity Parent;
  22.         public bool Html;
  23.         public int Line;
  24.         public char Lastchar;
  25.         public bool IsWhitespace;
  26.         Encoding encoding;
  27.         Uri resolvedUri;
  28.         TextReader stm;
  29.         bool weOwnTheStream;
  30.         int lineStart;
  31.         int absolutePos;
  32.         public Entity(string name, string pubid, string uri, string proxy) {
  33.             Name = name;
  34.             PublicId = pubid;
  35.             Uri = uri;
  36.             Proxy = proxy;
  37.             Html = (name != null && StringUtilities.EqualsIgnoreCase(name, "html"));
  38.         }
  39.         public Entity(string name, string literal) {
  40.             Name = name;
  41.             Literal = literal;
  42.             Internal = true;
  43.         }
  44.         public Entity(string name, Uri baseUri, TextReader stm, string proxy) {
  45.             Name = name;
  46.             Internal = true;
  47.             this.stm = stm;
  48.             this.resolvedUri = baseUri;
  49.             Proxy = proxy;
  50.             Html = (string.Compare(name, "html", true, CultureInfo.InvariantCulture) == 0);
  51.         }
  52.         public Uri ResolvedUri {
  53.             get {
  54.                 if (this.resolvedUri != null) return this.resolvedUri;
  55.                 else if (Parent != null) return Parent.ResolvedUri;
  56.                 return null;
  57.             }
  58.         }
  59.         public int LinePosition {
  60.             get { return this.absolutePos - this.lineStart + 1; }
  61.         }
  62.         public char ReadChar() {
  63.             char ch = (char)this.stm.Read();
  64.             if (ch == 0) {
  65.                 // convert nulls to whitespace, since they are not valid in XML anyway.
  66.                 ch = ' ';
  67.             }
  68.             this.absolutePos++;
  69.             if (ch == 0xa) {
  70.                 IsWhitespace = true;
  71.                 this.lineStart = this.absolutePos+1;
  72.                 Line++;
  73.             } 
  74.             else if (ch == ' ' || ch == 't') {
  75.                 IsWhitespace = true;
  76.                 if (Lastchar == 0xd) {
  77.                     this.lineStart = this.absolutePos;
  78.                     Line++;
  79.                 }
  80.             }
  81.             else if (ch == 0xd) {
  82.                 IsWhitespace = true;
  83.             }
  84.             else {
  85.                 IsWhitespace = false;
  86.                 if (Lastchar == 0xd) {
  87.                     Line++;
  88.                     this.lineStart = this.absolutePos;
  89.                 }
  90.             } 
  91.             Lastchar = ch;
  92.             return ch;
  93.         }
  94.         public void Open(Entity parent, Uri baseUri) {
  95.             Parent = parent;
  96.             if (parent != null) this.Html = parent.Html;
  97.             this.Line = 1;
  98.             if (Internal) {
  99.                 if (this.Literal != null)
  100.                     this.stm = new StringReader(this.Literal);
  101.             } 
  102.             else if (this.Uri == null) {
  103.                 this.Error("Unresolvable entity '{0}'", this.Name);
  104.             }
  105.             else {
  106.                 if (baseUri != null) {
  107.                     this.resolvedUri = new Uri(baseUri, this.Uri);
  108.                 }
  109.                 else {
  110.                     this.resolvedUri = new Uri(this.Uri);
  111.                 }
  112.                 Stream stream = null;
  113.                 Encoding e = Encoding.Default;
  114.                 switch (this.resolvedUri.Scheme) {
  115.                     case "file": {
  116.                             string path = this.resolvedUri.LocalPath;
  117.                             stream = new FileStream(path, FileMode.Open, FileAccess.Read);
  118.                         }
  119.                         break;
  120.                     default:
  121.                         //Console.WriteLine("Fetching:" + ResolvedUri.AbsoluteUri);
  122.                         HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(ResolvedUri);
  123.                         wr.UserAgent = "Mozilla/4.0 (compatible;);";
  124.                         wr.Timeout = 10000; // in case this is running in an ASPX page.
  125.                         if (Proxy != null) wr.Proxy = new WebProxy(Proxy);
  126.                         wr.PreAuthenticate = false; 
  127.                         // Pass the credentials of the process. 
  128.                         wr.Credentials = CredentialCache.DefaultCredentials; 
  129.                         WebResponse resp = wr.GetResponse();
  130.                         Uri actual = resp.ResponseUri;
  131.                         if (actual.AbsoluteUri != this.resolvedUri.AbsoluteUri) {
  132.                             this.resolvedUri = actual;
  133.                         }                       
  134.                         string contentType = resp.ContentType.ToLower();
  135.                         string mimeType = contentType;
  136.                         int i = contentType.IndexOf(';');
  137.                         if (i >= 0) {
  138.                             mimeType = contentType.Substring(0, i);
  139.                         }
  140.                         if (StringUtilities.EqualsIgnoreCase(mimeType, "text/html")){
  141.                             this.Html = true;
  142.                         }
  143.                         i = contentType.IndexOf("charset");
  144.                         e = Encoding.Default;
  145.                         if (i >= 0) {                                
  146.                             int j = contentType.IndexOf("=", i);
  147.                             int k = contentType.IndexOf(";", j);
  148.                             if (k<0) k = contentType.Length;
  149.                             if (j > 0) {
  150.                                 j++;
  151.                                 string charset = contentType.Substring(j, k - j).Trim();
  152.                                 try {
  153.                                     e = Encoding.GetEncoding(charset);
  154.                                 } catch (Exception) {
  155.                                 }
  156.                             }
  157.                         }
  158.                         stream = resp.GetResponseStream();
  159.                         break;
  160.                 }
  161.                 this.weOwnTheStream = true;
  162.                 HtmlStream html = new HtmlStream(stream, e);
  163.                 this.encoding = html.Encoding;
  164.                 this.stm = html;
  165.             }
  166.         }
  167.         public Encoding GetEncoding(){
  168.             return this.encoding;
  169.         }
  170.         
  171.         public void Close() {
  172.             if (this.weOwnTheStream) 
  173.                 this.stm.Close();
  174.         }
  175.         public char SkipWhitespace() {
  176.             char ch = Lastchar;
  177.             while (ch != Entity.EOF && (ch == ' ' || ch == 'r' || ch == 'n' || ch == 't')) {
  178.                 ch = ReadChar();
  179.             }
  180.             return ch;
  181.         }
  182.         public string ScanToken(StringBuilder sb, string term, bool nmtoken) {
  183.             sb.Length = 0;
  184.             char ch = Lastchar;
  185.             if (nmtoken && ch != '_' && !Char.IsLetter(ch)) {
  186.                 throw new Exception(
  187.                     String.Format("Invalid name start character '{0}'", ch));
  188.             }
  189.             while (ch != Entity.EOF && term.IndexOf(ch)<0) {
  190.                 if (!nmtoken || ch == '_' || ch == '.' || ch == '-' || ch == ':' || Char.IsLetterOrDigit(ch)) {
  191.                     sb.Append(ch);
  192.                 } 
  193.                 else {
  194.                     throw new Exception(
  195.                         String.Format("Invalid name character '{0}'", ch));
  196.                 }
  197.                 ch = ReadChar();
  198.             }
  199.             return sb.ToString();
  200.         }
  201.         public string ScanLiteral(StringBuilder sb, char quote) {
  202.             sb.Length = 0;
  203.             char ch = ReadChar();
  204.             while (ch != Entity.EOF && ch != quote ) {
  205.                 if (ch == '&') {
  206.                     ch = ReadChar();
  207.                     if (ch == '#') {
  208.                         string charent = ExpandCharEntity();
  209.                         sb.Append(charent);
  210.                         ch = this.Lastchar;
  211.                     } 
  212.                     else {
  213.                         sb.Append('&');
  214.                         sb.Append(ch);
  215.                         ch = ReadChar();
  216.                     }
  217.                 }               
  218.                 else {
  219.                     sb.Append(ch);
  220.                     ch = ReadChar();
  221.                 }
  222.             }
  223.             ReadChar(); // consume end quote.           
  224.             return sb.ToString();
  225.         }
  226.         public string ScanToEnd(StringBuilder sb, string type, string terminators) {
  227.             if (sb != null) sb.Length = 0;
  228.             int start = Line;
  229.             // This method scans over a chunk of text looking for the
  230.             // termination sequence specified by the 'terminators' parameter.
  231.             char ch = ReadChar();            
  232.             int state = 0;
  233.             char next = terminators[state];
  234.             while (ch != Entity.EOF) {
  235.                 if (ch == next) {
  236.                     state++;
  237.                     if (state >= terminators.Length) {
  238.                         // found it!
  239.                         break;
  240.                     }
  241.                     next = terminators[state];
  242.                 } 
  243.                 else if (state > 0) {
  244.                     // char didn't match, so go back and see how much does still match.
  245.                     int i = state - 1;
  246.                     int newstate = 0;
  247.                     while (i>=0 && newstate==0) {
  248.                         if (terminators[i] == ch) {
  249.                             // character is part of the terminators pattern, ok, so see if we can
  250.                             // match all the way back to the beginning of the pattern.
  251.                             int j = 1;
  252.                             while( i-j>=0) {
  253.                                 if (terminators[i-j] != terminators[state-j])
  254.                                     break;
  255.                                 j++;
  256.                             }
  257.                             if (j>i) {
  258.                                 newstate = i+1;
  259.                             }
  260.                         } 
  261.                         else {
  262.                             i--;
  263.                         }
  264.                     }
  265.                     if (sb != null) {
  266.                         i = (i<0) ? 1 : 0;
  267.                         for (int k = 0; k <= state-newstate-i; k++) {
  268.                             sb.Append(terminators[k]); 
  269.                         }
  270.                         if (i>0) // see if we've matched this char or not
  271.                             sb.Append(ch); // if not then append it to buffer.
  272.                     }
  273.                     state = newstate;
  274.                     next = terminators[newstate];
  275.                 }
  276.                 else {
  277.                     if (sb != null) sb.Append(ch);
  278.                 }
  279.                 ch = ReadChar();
  280.             }
  281.             if (ch == 0) Error(type + " starting on line {0} was never closed", start);
  282.             ReadChar(); // consume last char in termination sequence.
  283.             if (sb != null) return sb.ToString();
  284.             return "";
  285.         }
  286.         public string ExpandCharEntity() {
  287.             char ch = ReadChar();
  288.             int v = 0;
  289.             if (ch == 'x') {
  290.                 ch = ReadChar();
  291.                 for (; ch != Entity.EOF && ch != ';'; ch = ReadChar()) {
  292.                     int p = 0;
  293.                     if (ch >= '0' && ch <= '9') {
  294.                         p = (int)(ch-'0');
  295.                     } 
  296.                     else if (ch >= 'a' && ch <= 'f') {
  297.                         p = (int)(ch-'a')+10;
  298.                     } 
  299.                     else if (ch >= 'A' && ch <= 'F') {
  300.                         p = (int)(ch-'A')+10;
  301.                     }
  302.                     else {
  303.                         break;//we must be done!
  304.                         //Error("Hex digit out of range '{0}'", (int)ch);
  305.                     }
  306.                     v = (v*16)+p;
  307.                 }
  308.             } 
  309.             else {                   
  310.                 for (; ch != Entity.EOF && ch != ';'; ch = ReadChar()) {
  311.                     if (ch >= '0' && ch <= '9') {
  312.                         v = (v*10)+(int)(ch-'0');
  313.                     } 
  314.                     else {
  315.                         break; // we must be done!
  316.                         //Error("Decimal digit out of range '{0}'", (int)ch);
  317.                     }
  318.                 }
  319.             }
  320.             if (ch == 0) {
  321.                 Error("Premature {0} parsing entity reference", ch);
  322.             } else if (ch == ';') {
  323.                 ReadChar(); 
  324.             }
  325.             // HACK ALERT: IE and Netscape map the unicode characters 
  326.             if (this.Html && v >= 0x80 & v <= 0x9F) {
  327.                 // This range of control characters is mapped to Windows-1252!
  328.                 int size = CtrlMap.Length;
  329.                 int i = v-0x80;
  330.                 int unicode = CtrlMap[i];
  331.                 return Convert.ToChar(unicode).ToString();
  332.             }
  333.             return Convert.ToChar(v).ToString();
  334.         }
  335.         static int[] CtrlMap = new int[] {
  336.                                              // This is the windows-1252 mapping of the code points 0x80 through 0x9f.
  337.                                              8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141,
  338.                                              381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 
  339.                                              339, 157, 382, 376
  340.                                          };
  341.         public void Error(string msg) {
  342.             throw new Exception(msg);
  343.         }
  344.         public void Error(string msg, char ch) {
  345.             string str = (ch == Entity.EOF) ? "EOF" : Char.ToString(ch);            
  346.             throw new Exception(String.Format(msg, str));
  347.         }
  348.         public void Error(string msg, int x) {
  349.             throw new Exception(String.Format(msg, x));
  350.         }
  351.         public void Error(string msg, string arg) {
  352.             throw new Exception(String.Format(msg, arg));
  353.         }
  354.         public string Context() {
  355.             Entity p = this;
  356.             StringBuilder sb = new StringBuilder();
  357.             while (p != null) {
  358.                 string msg;
  359.                 if (p.Internal) {
  360.                     msg = String.Format("nReferenced on line {0}, position {1} of internal entity '{2}'", p.Line, p.LinePosition, p.Name);
  361.                 } 
  362.                 else {
  363.                     msg = String.Format("nReferenced on line {0}, position {1} of '{2}' entity at [{3}]", p.Line, p.LinePosition, p.Name, p.ResolvedUri.AbsolutePath);
  364.                 }
  365.                 sb.Append(msg);
  366.                 p = p.Parent;
  367.             }
  368.             return sb.ToString();
  369.         }
  370.         public static bool IsLiteralType(string token) {
  371.             return (token == "CDATA" || token == "SDATA" || token == "PI");
  372.         }
  373.         public void SetLiteralType(string token) {
  374.             switch (token) {
  375.                 case "CDATA":
  376.                     LiteralType = LiteralType.CDATA;
  377.                     break;
  378.                 case "SDATA":
  379.                     LiteralType = LiteralType.SDATA;
  380.                     break;
  381.                 case "PI":
  382.                     LiteralType = LiteralType.PI;
  383.                     break;
  384.             }
  385.         }
  386.     }
  387.     // This class decodes an HTML/XML stream correctly.
  388.     internal class HtmlStream : TextReader {
  389.         Stream stm;
  390.         byte[] rawBuffer;
  391.         int rawPos;
  392.         int rawUsed;
  393.         Encoding encoding;
  394.         Decoder decoder;
  395.         char[] buffer;
  396.         int used;
  397.         int pos;
  398.         private const int BUFSIZE = 16384;
  399.         private const int EOF = -1;
  400.         public HtmlStream(Stream stm, Encoding defaultEncoding) {            
  401.             if (defaultEncoding == null) defaultEncoding = Encoding.UTF8; // default is UTF8
  402.             if (!stm.CanSeek){
  403.                 // Need to be able to seek to sniff correctly.
  404.                 stm = CopyToMemoryStream(stm);
  405.             }
  406.             this.stm = stm;
  407.             rawBuffer = new Byte[BUFSIZE];
  408.             rawUsed = stm.Read(rawBuffer, 0, 4); // maximum byte order mark
  409.             this.buffer = new char[BUFSIZE];
  410.             // Check byte order marks
  411.             this.decoder = AutoDetectEncoding(rawBuffer, ref rawPos, rawUsed);
  412.             int bom = rawPos;
  413.             if (this.decoder == null) {
  414.                 this.decoder = defaultEncoding.GetDecoder();
  415.                 rawUsed += stm.Read(rawBuffer, 4, BUFSIZE-4);                
  416.                 DecodeBlock();
  417.                 // Now sniff to see if there is an XML declaration or HTML <META> tag.
  418.                 Decoder sd = SniffEncoding();
  419.                 if (sd != null) {
  420.                     this.decoder = sd;
  421.                 }
  422.             }            
  423.             // Reset to get ready for Read()
  424.             this.stm.Seek(0, SeekOrigin.Begin);
  425.             this.pos = this.used = 0;
  426.             // skip bom
  427.             if (bom>0){
  428.                 stm.Read(this.rawBuffer, 0, bom);
  429.             }
  430.             this.rawPos = this.rawUsed = 0;
  431.             
  432.         }
  433.         public Encoding Encoding {
  434.             get {
  435.                 return this.encoding;
  436.             }
  437.         }
  438.         Stream CopyToMemoryStream(Stream s){
  439.             int size = 100000; // large heap is more efficient
  440.             byte[] buffer = new byte[size];
  441.             int len;
  442.             MemoryStream r = new MemoryStream();
  443.             while ((len = s.Read(buffer, 0, size))>0){
  444.                 r.Write(buffer, 0, len);
  445.             }
  446.             r.Seek(0, SeekOrigin.Begin);                            
  447.             s.Close();
  448.             return r;
  449.         }
  450.         internal void DecodeBlock() {
  451.             // shift current chars to beginning.
  452.             if (pos > 0) {
  453.                 if (pos < used) {
  454.                     System.Array.Copy(buffer, pos, buffer, 0, used - pos);
  455.                 }
  456.                 used -= pos;
  457.                 pos = 0;
  458.             }
  459.             int len = decoder.GetCharCount(rawBuffer, rawPos, rawUsed - rawPos);
  460.             int available = buffer.Length - used;
  461.             if (available < len) {
  462.                 char[] newbuf = new char[buffer.Length + len];
  463.                 System.Array.Copy(buffer, pos, newbuf, 0, used - pos);
  464.                 buffer = newbuf;
  465.             }
  466.             used = pos + decoder.GetChars(rawBuffer, rawPos, rawUsed - rawPos, buffer, pos);
  467.             rawPos = rawUsed; // consumed the whole buffer!
  468.         }
  469.         internal static Decoder AutoDetectEncoding(byte[] buffer, ref int index, int length) {
  470.             if (4 <= (length - index)) {
  471.                 uint w = (uint)buffer[index + 0] << 24 | (uint)buffer[index + 1] << 16 | (uint)buffer[index + 2] << 8 | (uint)buffer[index + 3];
  472.                 // see if it's a 4-byte encoding
  473.                 switch (w) {
  474.                     case 0xfefffeff: 
  475.                         index += 4; 
  476.                         return new Ucs4DecoderBigEngian();
  477.                     case 0xfffefffe: 
  478.                         index += 4; 
  479.                         return new Ucs4DecoderLittleEndian();
  480.                     case 0x3c000000: 
  481.                         goto case 0xfefffeff;
  482.                     case 0x0000003c: 
  483.                         goto case 0xfffefffe;
  484.                 }
  485.                 w >>= 8;
  486.                 if (w == 0xefbbbf) {
  487.                     index += 3;
  488.                     return Encoding.UTF8.GetDecoder();
  489.                 }
  490.                 w >>= 8;
  491.                 switch (w) {
  492.                     case 0xfeff: 
  493.                         index += 2; 
  494.                         return UnicodeEncoding.BigEndianUnicode.GetDecoder();
  495.                     case 0xfffe: 
  496.                         index += 2; 
  497.                         return new UnicodeEncoding(false, false).GetDecoder();
  498.                     case 0x3c00: 
  499.                         goto case 0xfeff;
  500.                     case 0x003c: 
  501.                         goto case 0xfffe;
  502.                 }
  503.             }
  504.             return null;
  505.         }
  506.         private int ReadChar() {
  507.             // Read only up to end of current buffer then stop.
  508.             if (pos < used) return buffer[pos++];
  509.             return EOF;
  510.         }
  511.         private int PeekChar() {
  512.             int ch = ReadChar();
  513.             if (ch != EOF) {
  514.                 pos--;
  515.             }
  516.             return ch;
  517.         }
  518.         private bool SniffPattern(string pattern) {
  519.             int ch = PeekChar();
  520.             if (ch != pattern[0]) return false;
  521.             for (int i = 0, n = pattern.Length; ch != EOF && i < n; i++) {
  522.                 ch = ReadChar();
  523.                 char m = pattern[i];
  524.                 if (ch != m) {
  525.                     return false;
  526.                 }
  527.             }
  528.             return true;
  529.         }
  530.         private void SniffWhitespace() {
  531.             char ch = (char)PeekChar();
  532.             while (ch == ' ' || ch == 't' || ch == 'r' || ch == 'n') {
  533.                 int i = pos;
  534.                 ch = (char)ReadChar();
  535.                 if (ch != ' ' && ch != 't' && ch != 'r' && ch != 'n')
  536.                     pos = i;
  537.             }
  538.         }
  539.         private string SniffLiteral() {
  540.             int quoteChar = PeekChar();
  541.             if (quoteChar == ''' || quoteChar == '"') {
  542.                 ReadChar();// consume quote char
  543.                 int i = this.pos;
  544.                 int ch = ReadChar();
  545.                 while (ch != EOF && ch != quoteChar) {
  546.                     ch = ReadChar();
  547.                 }
  548.                 return (pos>i) ? new string(buffer, i, pos - i - 1) : "";
  549.             }
  550.             return null;
  551.         }
  552.         private string SniffAttribute(string name) {
  553.             SniffWhitespace();
  554.             string id = SniffName();
  555.             if (name == id){
  556.                 SniffWhitespace();
  557.                 if (SniffPattern("=")) {
  558.                     SniffWhitespace();
  559.                     return SniffLiteral();
  560.                 }
  561.             }
  562.             return null;
  563.         }
  564.         private string SniffAttribute(out string name) {
  565.             SniffWhitespace();
  566.             name = SniffName();
  567.             if (name != null){
  568.                 SniffWhitespace();
  569.                 if (SniffPattern("=")) {
  570.                     SniffWhitespace();
  571.                     return SniffLiteral();
  572.                 }
  573.             }
  574.             return null;
  575.         }
  576.         private void SniffTerminator(string term) {
  577.             int ch = ReadChar();
  578.             int i = 0;
  579.             int n = term.Length;
  580.             while (i < n && ch != EOF) {
  581.                 if (term[i] == ch) {
  582.                     i++;
  583.                     if (i == n) break;
  584.                 } else {
  585.                     i = 0; // reset.
  586.                 }
  587.                 ch = ReadChar();
  588.             }
  589.         }
  590.         internal Decoder  SniffEncoding() {
  591.             Decoder decoder = null;
  592.             if (SniffPattern("<?xml")) {
  593.                 string version = SniffAttribute("version");
  594.                 if (version != null) {
  595.                     string encoding = SniffAttribute("encoding");
  596.                     if (encoding != null) {
  597.                         try {
  598.                             Encoding enc = Encoding.GetEncoding(encoding);
  599.                             if (enc != null) {
  600.                                 this.encoding = enc;
  601.                                 return enc.GetDecoder();
  602.                             }
  603.                         } catch (Exception) {
  604.                             // oh well then.
  605.                         }
  606.                     }
  607.                     SniffTerminator(">");
  608.                 }
  609.             } 
  610.             if (decoder == null) {
  611.                 return SniffMeta();
  612.             }
  613.             return null;
  614.         }
  615.         internal Decoder SniffMeta(){
  616.             int i = ReadChar();            
  617.             while (i != EOF){
  618.                 char ch = (char)i;
  619.                 if (ch == '<') {
  620.                     string name = SniffName();
  621.                     if (name != null && StringUtilities.EqualsIgnoreCase(name, "meta")){
  622.                         string httpequiv = null;
  623.                         string content = null;
  624.                         while (true) {
  625.                             string value = SniffAttribute(out name);
  626.                             if (name == null) {
  627.                                 break;
  628.                             }
  629.                             if (StringUtilities.EqualsIgnoreCase(name, "http-equiv")){
  630.                                 httpequiv = value;
  631.                             } else if (StringUtilities.EqualsIgnoreCase(name, "content")){
  632.                                 content = value;
  633.                             }
  634.                         }
  635.                         if (httpequiv != null && StringUtilities.EqualsIgnoreCase(httpequiv, "content-type") && content != null){
  636.                             int j = content.IndexOf("charset");
  637.                             if (j>=0){
  638.                                 //charset=utf-8
  639.                                 j = content.IndexOf("=", j);
  640.                                 if (j>=0) {
  641.                                     j++;
  642.                                     int k = content.IndexOf(";", j);
  643.                                     if (k<0) k = content.Length;
  644.                                     string charset = content.Substring(j, k-j).Trim();
  645.                                     try {
  646.                                         Encoding e = Encoding.GetEncoding(charset);
  647.                                         this.encoding = e;
  648.                                         return e.GetDecoder();
  649.                                     } catch {
  650.                                     }
  651.                                 }                                
  652.                             }
  653.                         }
  654.                     }
  655.                 }
  656.                 i = ReadChar();
  657.             }
  658.             return null;
  659.         }
  660.         internal string SniffName(){
  661.             int c = PeekChar();
  662.             if (c == EOF)
  663.                 return null;
  664.             char ch = (char)c;
  665.             int start = pos;
  666.             while (pos < used - 1 && ( Char.IsLetterOrDigit(ch) || ch == '-' || ch == '_' || ch == ':')) {
  667.                 ch = buffer[++pos];
  668.             }
  669.             if (start == pos) return null;
  670.             return new string(buffer, start, pos-start);
  671.         }
  672.         internal void SkipWhitespace() {
  673.             char ch = (char)PeekChar();
  674.             while (pos < used - 1 && (ch == ' ' || ch == 'r' || ch == 'n')) {
  675.                 ch = buffer[++pos];
  676.             }
  677.         }
  678.         internal void SkipTo(char what) {
  679.             char ch = (char)PeekChar();
  680.             while (pos < used - 1 && (ch != what)) {
  681.                 ch = buffer[++pos];
  682.             }
  683.         }
  684.         internal string ParseAttribute() {
  685.             SkipTo('=');
  686.             if (pos < used) {
  687.                 pos++;
  688.                 SkipWhitespace();
  689.                 if (pos < used) {
  690.                     char quote = buffer[pos];
  691.                     pos++;
  692.                     int start = pos;
  693.                     SkipTo(quote);
  694.                     if (pos < used) {
  695.                         string result = new string(buffer, start, pos - start);
  696.                         pos++;
  697.                         return result;
  698.                     }
  699.                 }
  700.             }
  701.             return null;
  702.         }
  703.         public override int Peek() {
  704.             int result = Read();
  705.             if (result != EOF) {
  706.                 pos--;
  707.             }
  708.             return result;
  709.         }
  710.         public override int Read() {
  711.             if (pos == used) {
  712.                 rawUsed = stm.Read(rawBuffer, 0, rawBuffer.Length);
  713.                 rawPos = 0;
  714.                 if (rawUsed == 0) return EOF;
  715.                 DecodeBlock();
  716.             }
  717.             if (pos < used) return buffer[pos++];
  718.             return -1;
  719.         }
  720.         public override int Read(char[] buffer, int start, int length) {
  721.             if (pos == used) {
  722.                 rawUsed = stm.Read(rawBuffer, 0, rawBuffer.Length);
  723.                 rawPos = 0;
  724.                 if (rawUsed == 0) return -1;
  725.                 DecodeBlock();
  726.             }
  727.             if (pos < used) {
  728.                 length = Math.Min(used - pos, length);
  729.                 Array.Copy(this.buffer, pos, buffer, start, length);
  730.                 pos += length;
  731.                 return length;
  732.             }
  733.             return 0;
  734.         }
  735.         public override int ReadBlock(char[] buffer, int index, int count) {
  736.             return Read(buffer, index, count);
  737.         }
  738.         // Read up to end of line, or full buffer, whichever comes first.
  739.         public int ReadLine(char[] buffer, int start, int length) {
  740.             int i = 0;
  741.             int ch = ReadChar();
  742.             while (ch != EOF) {
  743.                 buffer[i+start] = (char)ch;
  744.                 i++;
  745.                 if (i+start == length) 
  746.                     break; // buffer is full
  747.                 if (ch == 'r' ) {
  748.                     if (PeekChar() == 'n') {
  749.                         ch = ReadChar();
  750.                         buffer[i + start] = (char)ch;
  751.                         i++;
  752.                     }
  753.                     break;
  754.                 } else if (ch == 'n') {
  755.                     break;
  756.                 }
  757.                 ch = ReadChar();
  758.             }
  759.             return i;
  760.         }
  761.         public override string ReadToEnd() {
  762.             char[] buffer = new char[100000]; // large block heap is more efficient
  763.             int len = 0;
  764.             StringBuilder sb = new StringBuilder();
  765.             while ((len = Read(buffer, 0, buffer.Length)) > 0) {
  766.                 sb.Append(buffer, 0, len);
  767.             }
  768.             return sb.ToString();
  769.         }
  770.         public override void Close() {
  771.             stm.Close();
  772.         }
  773.     }
  774.     internal abstract class Ucs4Decoder : Decoder {
  775.         internal byte[] temp = new byte[4];
  776.         internal int tempBytes = 0;
  777.         public override int GetCharCount(byte[] bytes, int index, int count) {
  778.             return (count + tempBytes) / 4;
  779.         }
  780.         internal abstract int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex);
  781.         public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) {
  782.             int i = tempBytes;
  783.             if (tempBytes > 0) {
  784.                 for (; i < 4; i++) {
  785.                     temp[i] = bytes[byteIndex];
  786.                     byteIndex++;
  787.                     byteCount--;
  788.                 }
  789.                 i = 1;
  790.                 GetFullChars(temp, 0, 4, chars, charIndex);
  791.                 charIndex++;
  792.             } else
  793.                 i = 0;
  794.             i = GetFullChars(bytes, byteIndex, byteCount, chars, charIndex) + i;
  795.             int j = (tempBytes + byteCount) % 4;
  796.             byteCount += byteIndex;
  797.             byteIndex = byteCount - j;
  798.             tempBytes = 0;
  799.             if (byteIndex >= 0)
  800.                 for (; byteIndex < byteCount; byteIndex++) {
  801.                     temp[tempBytes] = bytes[byteIndex];
  802.                     tempBytes++;
  803.                 }
  804.             return i;
  805.         }
  806.         internal char UnicodeToUTF16(UInt32 code) {
  807.             byte lowerByte, higherByte;
  808.             lowerByte = (byte)(0xD7C0 + (code >> 10));
  809.             higherByte = (byte)(0xDC00 | code & 0x3ff);
  810.             return ((char)((higherByte << 8) | lowerByte));
  811.         }
  812.     }
  813.     internal class Ucs4DecoderBigEngian : Ucs4Decoder {
  814.         internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) {
  815.             UInt32 code;
  816.             int i, j;
  817.             byteCount += byteIndex;
  818.             for (i = byteIndex, j = charIndex; i + 3 < byteCount; ) {
  819.                 code = (UInt32)(((bytes[i + 3]) << 24) | (bytes[i + 2] << 16) | (bytes[i + 1] << 8) | (bytes[i]));
  820.                 if (code > 0x10FFFF) {
  821.                     throw new Exception("Invalid character 0x" + code.ToString("x") + " in encoding");
  822.                 } else if (code > 0xFFFF) {
  823.                     chars[j] = UnicodeToUTF16(code);
  824.                     j++;
  825.                 } else {
  826.                     if (code >= 0xD800 && code <= 0xDFFF) {
  827.                         throw new Exception("Invalid character 0x" + code.ToString("x") + " in encoding");
  828.                     } else {
  829.                         chars[j] = (char)code;
  830.                     }
  831.                 }
  832.                 j++;
  833.                 i += 4;
  834.             }
  835.             return j - charIndex;
  836.         }
  837.     };
  838.     internal class Ucs4DecoderLittleEndian : Ucs4Decoder {
  839.         internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) {
  840.             UInt32 code;
  841.             int i, j;
  842.             byteCount += byteIndex;
  843.             for (i = byteIndex, j = charIndex; i + 3 < byteCount; ) {
  844.                 code = (UInt32)(((bytes[i]) << 24) | (bytes[i + 1] << 16) | (bytes[i + 2] << 8) | (bytes[i + 3]));
  845.                 if (code > 0x10FFFF) {
  846.                     throw new Exception("Invalid character 0x" + code.ToString("x") + " in encoding");
  847.                 } else if (code > 0xFFFF) {
  848.                     chars[j] = UnicodeToUTF16(code);
  849.                     j++;
  850.                 } else {
  851.                     if (code >= 0xD800 && code <= 0xDFFF) {
  852.                         throw new Exception("Invalid character 0x" + code.ToString("x") + " in encoding");
  853.                     } else {
  854.                         chars[j] = (char)code;
  855.                     }
  856.                 }
  857.                 j++;
  858.                 i += 4;
  859.             }
  860.             return j - charIndex;
  861.         }
  862.     }
  863.     public class ElementDecl
  864.     {
  865.         public ElementDecl(string name, bool sto, bool eto, ContentModel cm, string[] inclusions, string[] exclusions)
  866.         {
  867.             Name = name;
  868.             StartTagOptional = sto;
  869.             EndTagOptional = eto;
  870.             ContentModel = cm;
  871.             Inclusions = inclusions;
  872.             Exclusions = exclusions;
  873.         }
  874.         public string Name;
  875.         public bool StartTagOptional;
  876.         public bool EndTagOptional;
  877.         public ContentModel ContentModel;
  878.         public string[] Inclusions;
  879.         public string[] Exclusions;
  880.         public AttList AttList;
  881.         public AttDef FindAttribute(string name){
  882.             return AttList[name.ToUpper()];
  883.         }
  884.         public void AddAttDefs(AttList list)
  885.         {
  886.             if (AttList == null) 
  887.             {
  888.                 AttList = list;
  889.             } 
  890.             else 
  891.             {               
  892.                 foreach (AttDef a in list) 
  893.                 {
  894.                     if (AttList[a.Name] == null) 
  895.                     {
  896.                         AttList.Add(a);
  897.                     }
  898.                 }
  899.             }
  900.         }
  901.         public bool CanContain(string name, SgmlDtd dtd)
  902.         {            
  903.             // return true if this element is allowed to contain the given element.
  904.             if (Exclusions != null) 
  905.             {
  906.                 foreach (string s in Exclusions) 
  907.                 {
  908.                     if ((object)s == (object)name) // XmlNameTable optimization
  909.                         return false;
  910.                 }
  911.             }
  912.             if (Inclusions != null) 
  913.             {
  914.                 foreach (string s in Inclusions) 
  915.                 {
  916.                     if ((object)s == (object)name) // XmlNameTable optimization
  917.                         return true;
  918.                 }
  919.             }
  920.             return ContentModel.CanContain(name, dtd);
  921.         }
  922.     }
  923.     public enum DeclaredContent
  924.     {
  925.         Default, CDATA, RCDATA, EMPTY
  926.     }
  927.     public class ContentModel
  928.     {
  929.         public DeclaredContent DeclaredContent;
  930.         public int CurrentDepth;
  931.         public Group Model;
  932.         public ContentModel()
  933.         {
  934.             Model = new Group(null);
  935.         }
  936.         public void PushGroup()
  937.         {
  938.             Model = new Group(Model);
  939.             CurrentDepth++;
  940.         }
  941.         public int PopGroup()
  942.         {
  943.             if (CurrentDepth == 0) return -1;
  944.             CurrentDepth--;
  945.             Model.Parent.AddGroup(Model);
  946.             Model = Model.Parent;
  947.             return CurrentDepth;
  948.         }
  949.         public void AddSymbol(string sym)
  950.         {
  951.             Model.AddSymbol(sym);
  952.         }
  953.         public void AddConnector(char c)
  954.         {
  955.             Model.AddConnector(c);
  956.         }
  957.         public void AddOccurrence(char c)
  958.         {
  959.             Model.AddOccurrence(c);
  960.         }
  961.         public void SetDeclaredContent(string dc)
  962.         {
  963.             switch (dc) {
  964.                 case "EMPTY":
  965.                     this.DeclaredContent = DeclaredContent.EMPTY;
  966.                     break;
  967.                 case "RCDATA":
  968.                     this.DeclaredContent = DeclaredContent.RCDATA;
  969.                     break;
  970.                 case "CDATA":
  971.                     this.DeclaredContent = DeclaredContent.CDATA;
  972.                     break;
  973.                 default:
  974.                     throw new Exception(
  975.                         String.Format("Declared content type '{0}' is not supported", dc));
  976.             }
  977.         }
  978.         public bool CanContain(string name, SgmlDtd dtd)
  979.         {
  980.             if (DeclaredContent != DeclaredContent.Default)
  981.                 return false; // empty or text only node.
  982.             return Model.CanContain(name, dtd);
  983.         }
  984.     }
  985.     public enum GroupType 
  986.     {
  987.         None, And, Or, Sequence 
  988.     };
  989.     public enum Occurrence
  990.     {
  991.         Required, Optional, ZeroOrMore, OneOrMore
  992.     }
  993.     public class Group
  994.     {
  995.         public Group Parent;
  996.         public ArrayList Members;
  997.         public GroupType GroupType;
  998.         public Occurrence Occurrence;
  999.         public bool Mixed;
  1000.         public bool TextOnly {
  1001.             get { return this.Mixed && Members.Count == 0; }
  1002.         }
  1003.         public Group(Group parent)
  1004.         {
  1005.             Parent = parent;
  1006.             Members = new ArrayList();
  1007.             this.GroupType = GroupType.None;
  1008.             Occurrence = Occurrence.Required;
  1009.         }
  1010.         public void AddGroup(Group g)
  1011.         {
  1012.             Members.Add(g);
  1013.         }
  1014.         public void AddSymbol(string sym)
  1015.         {
  1016.             if (sym == "#PCDATA") 
  1017.             {               
  1018.                 Mixed = true;
  1019.             } 
  1020.             else 
  1021.             {
  1022.                 Members.Add(sym);
  1023.             }
  1024.         }
  1025.         public void AddConnector(char c)
  1026.         {
  1027.             if (!Mixed && Members.Count == 0) 
  1028.             {
  1029.                 throw new Exception(
  1030.                     String.Format("Missing token before connector '{0}'.", c)
  1031.                     );
  1032.             }
  1033.             GroupType gt = GroupType.None;
  1034.             switch (c) 
  1035.             {
  1036.                 case ',': 
  1037.                     gt = GroupType.Sequence;
  1038.                     break;
  1039.                 case '|':
  1040.                     gt = GroupType.Or;
  1041.                     break;
  1042.                 case '&':
  1043.                     gt = GroupType.And;
  1044.                     break;
  1045.             }
  1046.             if (GroupType != GroupType.None && GroupType != gt) 
  1047.             {
  1048.                 throw new Exception(
  1049.                     String.Format("Connector '{0}' is inconsistent with {1} group.", c, GroupType.ToString())
  1050.                     );
  1051.             }
  1052.             GroupType = gt;
  1053.         }
  1054.         public void AddOccurrence(char c)
  1055.         {
  1056.             Occurrence o = Occurrence.Required;
  1057.             switch (c) 
  1058.             {
  1059.                 case '?': 
  1060.                     o = Occurrence.Optional;
  1061.                     break;
  1062.                 case '+':
  1063.                     o = Occurrence.OneOrMore;
  1064.                     break;
  1065.                 case '*':
  1066.                     o = Occurrence.ZeroOrMore;
  1067.                     break;
  1068.             }
  1069.             Occurrence = o;
  1070.         }
  1071.         // Rough approximation - this is really assuming an "Or" group
  1072.         public bool CanContain(string name, SgmlDtd dtd)
  1073.         {
  1074.             // Do a simple search of members.
  1075.             foreach (object obj in Members) 
  1076.             {
  1077.                 if (obj is String) 
  1078.                 {
  1079.                     if (obj == (object)name) // XmlNameTable optimization
  1080.                         return true;
  1081.                 } 
  1082.             }
  1083.             // didn't find it, so do a more expensive search over child elements
  1084.             // that have optional start tags and over child groups.
  1085.             foreach (object obj in Members) 
  1086.             {
  1087.                 if (obj is String) 
  1088.                 {
  1089.                     string s = (string)obj;
  1090.                     ElementDecl e = dtd.FindElement(s);
  1091.                     if (e != null) 
  1092.                     {
  1093.                         if (e.StartTagOptional) 
  1094.                         {
  1095.                             // tricky case, the start tag is optional so element may be
  1096.                             // allowed inside this guy!
  1097.                             if (e.CanContain(name, dtd))
  1098.                                 return true;
  1099.                         }
  1100.                     }
  1101.                 } 
  1102.                 else 
  1103.                 {
  1104.                     Group m = (Group)obj;
  1105.                     if (m.CanContain(name, dtd)) 
  1106.                         return true;
  1107.                 }
  1108.             }
  1109.             return false;
  1110.         }
  1111.     }
  1112.     public enum AttributeType
  1113.     {
  1114.         DEFAULT, CDATA, ENTITY, ENTITIES, ID, IDREF, IDREFS, NAME, NAMES, NMTOKEN, NMTOKENS, 
  1115.         NUMBER, NUMBERS, NUTOKEN, NUTOKENS, NOTATION, ENUMERATION
  1116.     }
  1117.     public enum AttributePresence
  1118.     {
  1119.         DEFAULT, FIXED, REQUIRED, IMPLIED
  1120.     }
  1121.     public class AttDef
  1122.     {
  1123.         public string Name;
  1124.         public AttributeType Type;
  1125.         public string[] EnumValues;
  1126.         public string Default;
  1127.         public AttributePresence Presence;
  1128.         public AttDef(string name)
  1129.         {
  1130.             Name = name;
  1131.         }
  1132.         public void SetType(string type)
  1133.         {
  1134.             switch (type) 
  1135.             {
  1136.                 case "CDATA":
  1137.                     Type = AttributeType.CDATA;
  1138.                     break;
  1139.                 case "ENTITY":
  1140.                     Type = AttributeType.ENTITY;
  1141.                     break;
  1142.                 case "ENTITIES":
  1143.                     Type = AttributeType.ENTITIES;
  1144.                     break;
  1145.                 case "ID":
  1146.                     Type = AttributeType.ID;
  1147.                     break;
  1148.                 case "IDREF":
  1149.                     Type = AttributeType.IDREF;
  1150.                     break;
  1151.                 case "IDREFS":
  1152.                     Type = AttributeType.IDREFS;
  1153.                     break;
  1154.                 case "NAME":
  1155.                     Type = AttributeType.NAME;
  1156.                     break;
  1157.                 case "NAMES":
  1158.                     Type = AttributeType.NAMES;
  1159.                     break;
  1160.                 case "NMTOKEN":
  1161.                     Type = AttributeType.NMTOKEN;
  1162.                     break;
  1163.                 case "NMTOKENS":
  1164.                     Type = AttributeType.NMTOKENS;
  1165.                     break;
  1166.                 case "NUMBER":
  1167.                     Type = AttributeType.NUMBER;
  1168.                     break;
  1169.                 case "NUMBERS":
  1170.                     Type = AttributeType.NUMBERS;
  1171.                     break;
  1172.                 case "NUTOKEN":
  1173.                     Type = AttributeType.NUTOKEN;
  1174.                     break;
  1175.                 case "NUTOKENS":
  1176.                     Type = AttributeType.NUTOKENS;
  1177.                     break;
  1178.                 default:
  1179.                     throw new Exception("Attribute type '"+type+"' is not supported");
  1180.             }
  1181.         }
  1182.         public bool SetPresence (string token)
  1183.         {
  1184.             bool hasDefault = true;
  1185.             if (token == "FIXED") 
  1186.             {
  1187.                 Presence = AttributePresence.FIXED;             
  1188.             } 
  1189.             else if (token == "REQUIRED") 
  1190.             {
  1191.                 Presence = AttributePresence.REQUIRED;
  1192.                 hasDefault = false;
  1193.             }
  1194.             else if (token == "IMPLIED") 
  1195.             {
  1196.                 Presence = AttributePresence.IMPLIED;
  1197.                 hasDefault = false;
  1198.             }
  1199.             else 
  1200.             {
  1201.                 throw new Exception(String.Format("Attribute value '{0}' not supported", token));
  1202.             }
  1203.             return hasDefault;
  1204.         }
  1205.     }
  1206.     public class AttList : IEnumerable
  1207.     {
  1208.         Hashtable AttDefs;
  1209.         
  1210.         public AttList()
  1211.         {
  1212.             AttDefs = new Hashtable();
  1213.         }
  1214.         public void Add(AttDef a)
  1215.         {
  1216.             AttDefs.Add(a.Name, a);
  1217.         }
  1218.         public AttDef this[string name]
  1219.         {
  1220.             get 
  1221.             {
  1222.                 return (AttDef)AttDefs[name];
  1223.             }
  1224.         }
  1225.         public IEnumerator GetEnumerator()
  1226.         {
  1227.             return AttDefs.Values.GetEnumerator();
  1228.         }
  1229.     }
  1230.     public class SgmlDtd
  1231.     {
  1232.         public string Name;
  1233.         Hashtable elements;
  1234.         Hashtable pentities;
  1235.         Hashtable entities;
  1236.         StringBuilder sb;      
  1237.         Entity current;
  1238.         XmlNameTable nameTable;
  1239.         public SgmlDtd(string name, XmlNameTable nt)
  1240.         {
  1241.             this.nameTable = nt;
  1242.             this.Name = name;
  1243.             this.elements = new Hashtable();
  1244.             this.pentities = new Hashtable();
  1245.             this.entities = new Hashtable();
  1246.             this.sb = new StringBuilder();
  1247.         }
  1248.         public XmlNameTable NameTable { get { return this.nameTable; } }
  1249.         public static SgmlDtd Parse(Uri baseUri, string name, string pubid, string url, string subset, string proxy, XmlNameTable nt)
  1250.         {
  1251.             SgmlDtd dtd = new SgmlDtd(name, nt);
  1252.             if (url != null && url != "") 
  1253.             {
  1254.                 dtd.PushEntity(baseUri, new Entity(dtd.Name, pubid, url, proxy));
  1255.             }
  1256.             if (subset != null && subset != "") 
  1257.             {
  1258.                 dtd.PushEntity(baseUri, new Entity(name, subset));
  1259.             }
  1260.             try 
  1261.             {
  1262.                 dtd.Parse();
  1263.             } 
  1264.             catch (Exception e)
  1265.             {
  1266.                 throw new Exception(e.Message + dtd.current.Context());
  1267.             }           
  1268.             return dtd;
  1269.         }
  1270.         public static SgmlDtd Parse(Uri baseUri, string name, string pubid, TextReader input, string subset, string proxy, XmlNameTable nt) {
  1271.             SgmlDtd dtd = new SgmlDtd(name, nt);
  1272.             dtd.PushEntity(baseUri, new Entity(dtd.Name, baseUri, input, proxy));
  1273.             if (subset != null && subset != "") {
  1274.                 dtd.PushEntity(baseUri, new Entity(name, subset));
  1275.             }
  1276.             try {
  1277.                 dtd.Parse();
  1278.             } 
  1279.             catch (Exception e) {
  1280.                 throw new Exception(e.Message + dtd.current.Context());
  1281.             }           
  1282.             return dtd;
  1283.         }
  1284.         public Entity FindEntity(string name)
  1285.         {
  1286.             return (Entity)this.entities[name];
  1287.         }
  1288.         public ElementDecl FindElement(string name)
  1289.         {
  1290.             return (ElementDecl)this.elements[name.ToUpper()];
  1291.         }
  1292.         //-------------------------------- Parser -------------------------
  1293.         void PushEntity(Uri baseUri, Entity e)
  1294.         {
  1295.             e.Open(this.current, baseUri);
  1296.             this.current = e;
  1297.             this.current.ReadChar();
  1298.         }
  1299.         void PopEntity()
  1300.         {
  1301.             if (this.current != null) this.current.Close();
  1302.             if (this.current.Parent != null) 
  1303.             {
  1304.                 this.current = this.current.Parent;
  1305.             } 
  1306.             else 
  1307.             {
  1308.                 this.current = null;
  1309.             }
  1310.         }
  1311.         void Parse()
  1312.         {
  1313.             char ch = this.current.Lastchar;
  1314.             while (true) 
  1315.             {
  1316.                 switch (ch) 
  1317.                 {
  1318.                     case Entity.EOF:
  1319.                         PopEntity();
  1320.                         if (this.current == null)
  1321.                             return;
  1322.                         ch = this.current.Lastchar;
  1323.                         break;
  1324.                     case ' ':
  1325.                     case 'n':
  1326.                     case 'r':
  1327.                     case 't':
  1328.                         ch = this.current.ReadChar();
  1329.                         break;
  1330.                     case '<':
  1331.                         ParseMarkup();
  1332.                         ch = this.current.ReadChar();
  1333.                         break;
  1334.                     case '%':
  1335.                         Entity e = ParseParameterEntity(SgmlDtd.WhiteSpace);
  1336.                         try 
  1337.                         {
  1338.                             PushEntity(this.current.ResolvedUri, e);
  1339.                         } 
  1340.                         catch (Exception ex) 
  1341.                         {
  1342.                             // bugbug - need an error log.
  1343.                             Console.WriteLine(ex.Message + this.current.Context());
  1344.                         }
  1345.                         ch = this.current.Lastchar;
  1346.                         break;
  1347.                     default:
  1348.                         this.current.Error("Unexpected character '{0}'", ch);
  1349.                         break;
  1350.                 }               
  1351.             }
  1352.         }
  1353.         void ParseMarkup()
  1354.         {
  1355.             char ch = this.current.ReadChar();
  1356.             if (ch != '!') 
  1357.             {
  1358.                 this.current.Error("Found '{0}', but expecing declaration starting with '<!'");
  1359.                 return;
  1360.             }
  1361.             ch = this.current.ReadChar();
  1362.             if (ch == '-') 
  1363.             {
  1364.                 ch = this.current.ReadChar();
  1365.                 if (ch != '-') this.current.Error("Expecting comment '<!--' but found {0}", ch);
  1366.                 this.current.ScanToEnd(this.sb, "Comment", "-->");
  1367.             } 
  1368.             else if (ch == '[') 
  1369.             {
  1370.                 ParseMarkedSection();
  1371.             }
  1372.             else 
  1373.             {
  1374.                 string token = this.current.ScanToken(this.sb, SgmlDtd.WhiteSpace, true);
  1375.                 switch (token) 
  1376.                 {
  1377.                     case "ENTITY":
  1378.                         ParseEntity();
  1379.                         break;
  1380.                     case "ELEMENT":
  1381.                         ParseElementDecl();
  1382.                         break;
  1383.                     case "ATTLIST":
  1384.                         ParseAttList();
  1385.                         break;
  1386.                     default:
  1387.                         this.current.Error("Invalid declaration '<!{0}'.  Expecting 'ENTITY', 'ELEMENT' or 'ATTLIST'.", token);
  1388.                         break;
  1389.                 }
  1390.             }
  1391.         }
  1392.         char ParseDeclComments()
  1393.         {
  1394.             char ch = this.current.Lastchar;
  1395.             while (ch == '-') 
  1396.             {
  1397.                 ch = ParseDeclComment(true);
  1398.             }
  1399.             return ch;
  1400.         }
  1401.         char ParseDeclComment(bool full)
  1402.         {
  1403.             int start = this.current.Line;
  1404.             // -^-...--
  1405.             // This method scans over a comment inside a markup declaration.
  1406.             char ch = this.current.ReadChar();
  1407.             if (full && ch != '-') this.current.Error("Expecting comment delimiter '--' but found {0}", ch);
  1408.             this.current.ScanToEnd(this.sb, "Markup Comment", "--");
  1409.             return this.current.SkipWhitespace();
  1410.         }
  1411.         void ParseMarkedSection()
  1412.         {
  1413.             // <![^ name [ ... ]]>
  1414.             this.current.ReadChar(); // move to next char.
  1415.             string name = ScanName("[");
  1416.             if (name == "INCLUDE") 
  1417.             {
  1418.                 ParseIncludeSection();
  1419.             } 
  1420.             else if (name == "IGNORE") 
  1421.             {
  1422.                 ParseIgnoreSection();
  1423.             }
  1424.             else 
  1425.             {
  1426.                 this.current.Error("Unsupported marked section type '{0}'", name);
  1427.             }
  1428.         }
  1429.         void ParseIncludeSection()
  1430.         {
  1431.             throw new NotImplementedException("Include Section");
  1432.         }
  1433.         void ParseIgnoreSection()
  1434.         {
  1435.             int start = this.current.Line;
  1436.             // <!-^-...-->
  1437.             char ch = this.current.SkipWhitespace();
  1438.             if (ch != '[') this.current.Error("Expecting '[' but found {0}", ch);
  1439.             this.current.ScanToEnd(this.sb, "Conditional Section", "]]>");
  1440.         }
  1441.         string ScanName(string term)
  1442.         {
  1443.             // skip whitespace, scan name (which may be parameter entity reference
  1444.             // which is then expanded to a name)
  1445.             char ch = this.current.SkipWhitespace();
  1446.             if (ch == '%') 
  1447.             {
  1448.                 Entity e = ParseParameterEntity(term);
  1449.                 ch = this.current.Lastchar;
  1450.                 // bugbug - need to support external and nested parameter entities
  1451.                 if (!e.Internal) throw new NotSupportedException("External parameter entity resolution");
  1452.                 return e.Literal.Trim();
  1453.             } 
  1454.             else 
  1455.             {
  1456.                 return this.current.ScanToken(this.sb, term, true);
  1457.             }
  1458.         }
  1459.         Entity ParseParameterEntity(string term)
  1460.         {
  1461.             // almost the same as this.current.ScanToken, except we also terminate on ';'
  1462.             char ch = this.current.ReadChar();
  1463.             string name =  this.current.ScanToken(this.sb, ";"+term, false);
  1464.             name = this.nameTable.Add(name);
  1465.             if (this.current.Lastchar == ';') 
  1466.                 this.current.ReadChar();
  1467.             Entity e = GetParameterEntity(name);
  1468.             return e;
  1469.         }
  1470.         Entity GetParameterEntity(string name)
  1471.         {
  1472.             Entity e = (Entity)this.pentities[name];
  1473.             if (e == null) this.current.Error("Reference to undefined parameter entity '{0}'", name);
  1474.             return e;
  1475.         }
  1476.         
  1477.         static string WhiteSpace = " rnt";
  1478.         void ParseEntity()
  1479.         {
  1480.             char ch = this.current.SkipWhitespace();
  1481.             bool pe = (ch == '%');
  1482.             if (pe)
  1483.             {
  1484.                 // parameter entity.
  1485.                 this.current.ReadChar(); // move to next char
  1486.                 ch = this.current.SkipWhitespace();
  1487.             }
  1488.             string name = this.current.ScanToken(this.sb, SgmlDtd.WhiteSpace, true);
  1489.             name = this.nameTable.Add(name);
  1490.             ch = this.current.SkipWhitespace();
  1491.             Entity e = null;
  1492.             if (ch == '"' || ch == ''') 
  1493.             {
  1494.                 string literal = this.current.ScanLiteral(this.sb, ch);
  1495.                 e = new Entity(name, literal);                
  1496.             } 
  1497.             else 
  1498.             {
  1499.                 string pubid = null;
  1500.                 string extid = null;
  1501.                 string tok = this.current.ScanToken(this.sb, SgmlDtd.WhiteSpace, true);
  1502.                 if (Entity.IsLiteralType(tok) )
  1503.                 {
  1504.                     ch = this.current.SkipWhitespace();
  1505.                     string literal = this.current.ScanLiteral(this.sb, ch);
  1506.                     e = new Entity(name, literal);
  1507.                     e.SetLiteralType(tok);
  1508.                 }
  1509.                 else 
  1510.                 {
  1511.                     extid = tok;
  1512.                     if (extid == "PUBLIC") 
  1513.                     {
  1514.                         ch = this.current.SkipWhitespace();
  1515.                         if (ch == '"' || ch == ''') 
  1516.                         {
  1517.                             pubid = this.current.ScanLiteral(this.sb, ch);
  1518.                         } 
  1519.                         else 
  1520.                         {
  1521.                             this.current.Error("Expecting public identifier literal but found '{0}'",ch);
  1522.                         }
  1523.                     } 
  1524.                     else if (extid != "SYSTEM") 
  1525.                     {
  1526.                         this.current.Error("Invalid external identifier '{0}'.  Expecing 'PUBLIC' or 'SYSTEM'.", extid);
  1527.                     }
  1528.                     string uri = null;
  1529.                     ch = this.current.SkipWhitespace();
  1530.                     if (ch == '"' || ch == ''') 
  1531.                     {
  1532.                         uri = this.current.ScanLiteral(this.sb, ch);
  1533.                     } 
  1534.                     else if (ch != '>')
  1535.                     {
  1536.                         this.current.Error("Expecting system identifier literal but found '{0}'",ch);
  1537.                     }
  1538.                     e = new Entity(name, pubid, uri, this.current.Proxy);
  1539.                 }
  1540.             }
  1541.             ch = this.current.SkipWhitespace();
  1542.             if (ch == '-') 
  1543.                 ch = ParseDeclComments();
  1544.             if (ch != '>') 
  1545.             {
  1546.                 this.current.Error("Expecting end of entity declaration '>' but found '{0}'", ch);  
  1547.             }           
  1548.             if (pe) this.pentities.Add(e.Name, e);
  1549.             else this.entities.Add(e.Name, e);
  1550.         }
  1551.         void ParseElementDecl()
  1552.         {
  1553.             char ch = this.current.SkipWhitespace();
  1554.             string[] names = ParseNameGroup(ch, true);
  1555.             ch = Char.ToUpper(this.current.SkipWhitespace());
  1556.             bool sto = false;
  1557.             bool eto = false;
  1558.             if (ch == 'O' || ch == '-') {
  1559.                 sto = (ch == 'O'); // start tag optional?   
  1560.                 this.current.ReadChar();
  1561.                 ch = Char.ToUpper(this.current.SkipWhitespace());
  1562.                 if (ch == 'O' || ch == '-'){
  1563.                     eto = (ch == 'O'); // end tag optional? 
  1564.                     ch = this.current.ReadChar();
  1565.                 }
  1566.             }
  1567.             ch = this.current.SkipWhitespace();
  1568.             ContentModel cm = ParseContentModel(ch);
  1569.             ch = this.current.SkipWhitespace();
  1570.             string [] exclusions = null;
  1571.             string [] inclusions = null;
  1572.             if (ch == '-') 
  1573.             {
  1574.                 ch = this.current.ReadChar();
  1575.                 if (ch == '(') 
  1576.                 {
  1577.                     exclusions = ParseNameGroup(ch, true);
  1578.                     ch = this.current.SkipWhitespace();
  1579.                 }
  1580.                 else if (ch == '-') 
  1581.                 {
  1582.                     ch = ParseDeclComment(false);
  1583.                 } 
  1584.                 else 
  1585.                 {
  1586.                     this.current.Error("Invalid syntax at '{0}'", ch);  
  1587.                 }
  1588.             }
  1589.             if (ch == '-') 
  1590.                 ch = ParseDeclComments();
  1591.             if (ch == '+') 
  1592.             {
  1593.                 ch = this.current.ReadChar();
  1594.                 if (ch != '(') 
  1595.                 {
  1596.                     this.current.Error("Expecting inclusions name group", ch);  
  1597.                 }
  1598.                 inclusions = ParseNameGroup(ch, true);
  1599.                 ch = this.current.SkipWhitespace();
  1600.             }
  1601.             if (ch == '-') 
  1602.                 ch = ParseDeclComments();
  1603.             if (ch != '>') 
  1604.             {
  1605.                 this.current.Error("Expecting end of ELEMENT declaration '>' but found '{0}'", ch); 
  1606.             }
  1607.             foreach (string name in names) 
  1608.             {
  1609.                 string atom = name.ToUpper();
  1610.                 atom = this.nameTable.Add(name); 
  1611.                 this.elements.Add(atom, new ElementDecl(atom, sto, eto, cm, inclusions, exclusions));
  1612.             }
  1613.         }
  1614.         static string ngterm = " rnt|,)";
  1615.         string[] ParseNameGroup(char ch, bool nmtokens)
  1616.         {
  1617.             ArrayList names = new ArrayList();
  1618.             if (ch == '(') 
  1619.             {
  1620.                 ch = this.current.ReadChar();
  1621.                 ch = this.current.SkipWhitespace();
  1622.                 while (ch != ')') 
  1623.                 {
  1624.                     // skip whitespace, scan name (which may be parameter entity reference
  1625.                     // which is then expanded to a name)                    
  1626.                     ch = this.current.SkipWhitespace();
  1627.                     if (ch == '%') 
  1628.                     {
  1629.                         Entity e = ParseParameterEntity(SgmlDtd.ngterm);
  1630.                         PushEntity(this.current.ResolvedUri, e);
  1631.                         ParseNameList(names, nmtokens);
  1632.                         PopEntity();
  1633.                         ch = this.current.Lastchar;
  1634.                     }
  1635.                     else 
  1636.                     {
  1637.                         string token = this.current.ScanToken(this.sb, SgmlDtd.ngterm, nmtokens);
  1638.                         token = token.ToUpper();
  1639.                         string atom = this.nameTable.Add(token);
  1640.                         names.Add(atom);
  1641.                     }
  1642.                     ch = this.current.SkipWhitespace();
  1643.                     if (ch == '|' || ch == ',') ch = this.current.ReadChar();
  1644.                 }
  1645.                 this.current.ReadChar(); // consume ')'
  1646.             } 
  1647.             else 
  1648.             {
  1649.                 string name = this.current.ScanToken(this.sb, SgmlDtd.WhiteSpace, nmtokens);
  1650.                 name = name.ToUpper();
  1651.                 name = this.nameTable.Add(name);
  1652.                 names.Add(name);
  1653.             }
  1654.             return (string[])names.ToArray(typeof(String));
  1655.         }
  1656.         void ParseNameList(ArrayList names, bool nmtokens)
  1657.         {
  1658.             char ch = this.current.Lastchar;
  1659.             ch = this.current.SkipWhitespace();
  1660.             while (ch != Entity.EOF) 
  1661.             {
  1662.                 string name;
  1663.                 if (ch == '%') 
  1664.                 {
  1665.                     Entity e = ParseParameterEntity(SgmlDtd.ngterm);
  1666.                     PushEntity(this.current.ResolvedUri, e);
  1667.                     ParseNameList(names, nmtokens);
  1668.                     PopEntity();
  1669.                     ch = this.current.Lastchar;
  1670.                 } 
  1671.                 else 
  1672.                 {
  1673.                     name = this.current.ScanToken(this.sb, SgmlDtd.ngterm, true);
  1674.                     name = name.ToUpper();
  1675.                     name = this.nameTable.Add(name);
  1676.                     names.Add(name);
  1677.                 }
  1678.                 ch = this.current.SkipWhitespace();
  1679.                 if (ch == '|') 
  1680.                 {
  1681.                     ch = this.current.ReadChar();
  1682.                     ch = this.current.SkipWhitespace();
  1683.                 }
  1684.             }
  1685.         }
  1686.         static string dcterm = " rnt>";
  1687.         ContentModel ParseContentModel(char ch)
  1688.         {
  1689.             ContentModel cm = new ContentModel();
  1690.             if (ch == '(') 
  1691.             {
  1692.                 this.current.ReadChar();
  1693.                 ParseModel(')', cm);
  1694.                 ch = this.current.ReadChar();
  1695.                 if (ch == '?' || ch == '+' || ch == '*') 
  1696.                 {
  1697.                     cm.AddOccurrence(ch);
  1698.                     this.current.ReadChar();
  1699.                 }
  1700.             } 
  1701.             else if (ch == '%') 
  1702.             {
  1703.                 Entity e = ParseParameterEntity(SgmlDtd.dcterm);
  1704.                 PushEntity(this.current.ResolvedUri, e);
  1705.                 cm = ParseContentModel(this.current.Lastchar);
  1706.                 PopEntity(); // bugbug should be at EOF.
  1707.             }
  1708.             else
  1709.             {
  1710.                 string dc = ScanName(SgmlDtd.dcterm);
  1711.                 cm.SetDeclaredContent(dc);
  1712.             }
  1713.             return cm;
  1714.         }
  1715.         static string cmterm = " rnt,&|()?+*";
  1716.         void ParseModel(char cmt, ContentModel cm)
  1717.         {
  1718.             // Called when part of the model is made up of the contents of a parameter entity
  1719.             int depth = cm.CurrentDepth;
  1720.             char ch = this.current.Lastchar;
  1721.             ch = this.current.SkipWhitespace();
  1722.             while (ch != cmt || cm.CurrentDepth > depth) // the entity must terminate while inside the content model.
  1723.             {
  1724.                 if (ch == Entity.EOF) 
  1725.                 {
  1726.                     this.current.Error("Content Model was not closed");
  1727.                 }
  1728.                 if (ch == '%') 
  1729.                 {
  1730.                     Entity e = ParseParameterEntity(SgmlDtd.cmterm);
  1731.                     PushEntity(this.current.ResolvedUri, e);
  1732.                     ParseModel(Entity.EOF, cm);
  1733.                     PopEntity();                    
  1734.                     ch = this.current.SkipWhitespace();
  1735.                 } 
  1736.                 else if (ch == '(') 
  1737.                 {
  1738.                     cm.PushGroup();
  1739.                     this.current.ReadChar();// consume '('
  1740.                     ch = this.current.SkipWhitespace();
  1741.                 }
  1742.                 else if (ch == ')') 
  1743.                 {
  1744.                     ch = this.current.ReadChar();// consume ')'
  1745.                     if (ch == '*' || ch == '+' || ch == '?') 
  1746.                     {
  1747.                         cm.AddOccurrence(ch);
  1748.                         ch = this.current.ReadChar();
  1749.                     }
  1750.                     if (cm.PopGroup() < depth)
  1751.                     {
  1752.                         this.current.Error("Parameter entity cannot close a paren outside it's own scope");
  1753.                     }
  1754.                     ch = this.current.SkipWhitespace();
  1755.                 }
  1756.                 else if (ch == ',' || ch == '|' || ch == '&') 
  1757.                 {
  1758.                     cm.AddConnector(ch);
  1759.                     this.current.ReadChar(); // skip connector
  1760.                     ch = this.current.SkipWhitespace();
  1761.                 }
  1762.                 else
  1763.                 {
  1764.                     string token;
  1765.                     if (ch == '#') 
  1766.                     {
  1767.                         ch = this.current.ReadChar();
  1768.                         token = "#" + this.current.ScanToken(this.sb, SgmlDtd.cmterm, true); // since '#' is not a valid name character.
  1769.                     } 
  1770.                     else 
  1771.                     {
  1772.                         token = this.current.ScanToken(this.sb, SgmlDtd.cmterm, true);
  1773.                     }
  1774.                     token = token.ToUpper();
  1775.                     token = this.nameTable.Add(token);// atomize it.
  1776.                     ch = this.current.Lastchar;
  1777.                     if (ch == '?' || ch == '+' || ch == '*') 
  1778.                     {
  1779.                         cm.PushGroup();
  1780.                         cm.AddSymbol(token);
  1781.                         cm.AddOccurrence(ch);
  1782.                         cm.PopGroup();
  1783.                         this.current.ReadChar(); // skip connector
  1784.                         ch = this.current.SkipWhitespace();
  1785.                     } 
  1786.                     else 
  1787.                     {
  1788.                         cm.AddSymbol(token);
  1789.                         ch = this.current.SkipWhitespace();
  1790.                     }                   
  1791.                 }
  1792.             }
  1793.         }
  1794.         void ParseAttList()
  1795.         {
  1796.             char ch = this.current.SkipWhitespace();
  1797.             string[] names = ParseNameGroup(ch, true);          
  1798.             AttList attlist = new AttList();
  1799.             ParseAttList(attlist, '>');
  1800.             foreach (string name in names) 
  1801.             {
  1802.                 ElementDecl e = (ElementDecl)this.elements[name];
  1803.                 if (e == null) 
  1804.                 {
  1805.                     this.current.Error("ATTLIST references undefined ELEMENT {0}", name);
  1806.                 }
  1807.                 e.AddAttDefs(attlist);
  1808.             }
  1809.         }
  1810.         static string peterm = " trn>";
  1811.         void ParseAttList(AttList list, char term)
  1812.         {
  1813.             char ch = this.current.SkipWhitespace();
  1814.             while (ch != term) 
  1815.             {
  1816.                 if (ch == '%') 
  1817.                 {
  1818.                     Entity e = ParseParameterEntity(SgmlDtd.peterm);
  1819.                     PushEntity(this.current.ResolvedUri, e);
  1820.                     ParseAttList(list, Entity.EOF);
  1821.                     PopEntity();                    
  1822.                     ch = this.current.SkipWhitespace();
  1823.                 } 
  1824.                 else if (ch == '-') 
  1825.                 {
  1826.                     ch = ParseDeclComments();
  1827.                 }
  1828.                 else
  1829.                 {
  1830.                     AttDef a = ParseAttDef(ch);
  1831.                     list.Add(a);
  1832.                 }
  1833.                 ch = this.current.SkipWhitespace();
  1834.             }
  1835.         }
  1836.         AttDef ParseAttDef(char ch)
  1837.         {
  1838.             ch = this.current.SkipWhitespace();
  1839.             string name = ScanName(SgmlDtd.WhiteSpace);
  1840.             name = name.ToUpper();
  1841.             name = this.nameTable.Add(name);
  1842.             AttDef attdef = new AttDef(name);
  1843.             ch = this.current.SkipWhitespace();
  1844.             if (ch == '-') 
  1845.                 ch = ParseDeclComments();               
  1846.             ParseAttType(ch, attdef);
  1847.             ch = this.current.SkipWhitespace();
  1848.             if (ch == '-') 
  1849.                 ch = ParseDeclComments();               
  1850.             ParseAttDefault(ch, attdef);
  1851.             ch = this.current.SkipWhitespace();
  1852.             if (ch == '-') 
  1853.                 ch = ParseDeclComments();               
  1854.             return attdef;
  1855.         }
  1856.         void ParseAttType(char ch, AttDef attdef)
  1857.         {
  1858.             if (ch == '%')
  1859.             {
  1860.                 Entity e = ParseParameterEntity(SgmlDtd.WhiteSpace);
  1861.                 PushEntity(this.current.ResolvedUri, e);
  1862.                 ParseAttType(this.current.Lastchar, attdef);
  1863.                 PopEntity(); // bugbug - are we at the end of the entity?
  1864.                 ch = this.current.Lastchar;
  1865.                 return;
  1866.             }
  1867.             if (ch == '(') 
  1868.             {
  1869.                 attdef.EnumValues = ParseNameGroup(ch, false);  
  1870.                 attdef.Type = AttributeType.ENUMERATION;
  1871.             } 
  1872.             else 
  1873.             {
  1874.                 string token = ScanName(SgmlDtd.WhiteSpace);
  1875.                 if (token == "NOTATION") 
  1876.                 {
  1877.                     ch = this.current.SkipWhitespace();
  1878.                     if (ch != '(') 
  1879.                     {
  1880.                         this.current.Error("Expecting name group '(', but found '{0}'", ch);
  1881.                     }
  1882.                     attdef.Type = AttributeType.NOTATION;
  1883.                     attdef.EnumValues = ParseNameGroup(ch, true);
  1884.                 } 
  1885.                 else 
  1886.                 {
  1887.                     attdef.SetType(token);
  1888.                 }
  1889.             }
  1890.         }
  1891.         void ParseAttDefault(char ch, AttDef attdef)
  1892.         {
  1893.             if (ch == '%')
  1894.             {
  1895.                 Entity e = ParseParameterEntity(SgmlDtd.WhiteSpace);
  1896.                 PushEntity(this.current.ResolvedUri, e);
  1897.                 ParseAttDefault(this.current.Lastchar, attdef);
  1898.                 PopEntity(); // bugbug - are we at the end of the entity?
  1899.                 ch = this.current.Lastchar;
  1900.                 return;
  1901.             }
  1902.             bool hasdef = true;
  1903.             if (ch == '#') 
  1904.             {
  1905.                 this.current.ReadChar();
  1906.                 string token = this.current.ScanToken(this.sb, SgmlDtd.WhiteSpace, true);
  1907.                 hasdef = attdef.SetPresence(token);
  1908.                 ch = this.current.SkipWhitespace();
  1909.             } 
  1910.             if (hasdef) 
  1911.             {
  1912.                 if (ch == ''' || ch == '"') 
  1913.                 {
  1914.                     string lit = this.current.ScanLiteral(this.sb, ch);
  1915.                     attdef.Default = lit;
  1916.                     ch = this.current.SkipWhitespace();
  1917.                 }
  1918.                 else
  1919.                 {
  1920.                     string name = this.current.ScanToken(this.sb, SgmlDtd.WhiteSpace, false);
  1921.                     name = name.ToUpper();
  1922.                     name = this.nameTable.Add(name);
  1923.                     attdef.Default = name; // bugbug - must be one of the enumerated names.
  1924.                     ch = this.current.SkipWhitespace();
  1925.                 }
  1926.             }
  1927.         }
  1928.     }   
  1929.     class StringUtilities {
  1930.         public static bool EqualsIgnoreCase(string a, string b){
  1931.             return string.Compare(a, b, true, CultureInfo.InvariantCulture) == 0;
  1932.         }
  1933.     }
  1934. }