/// <summary> /// Skips the font awesome specification at the current specified pos. Pos is inside str at the '@'. font awesome /// directives are @fa-iconname. newPos is positioned to character after iconname if successful match. iconname scanned is specified in iconname. /// </summary> /// <param name="str">The string.</param> /// <param name="currentPos">The current position.</param> /// <param name="newPos">The new position.</param> /// <param name="iconName">Name of the icon.</param> /// <returns> /// bool if match was found and properly skipped, otherwise false. /// </returns> public static bool SkipFontAwesome(string str, int currentPos, out int newPos, out string iconName) { newPos = currentPos; iconName = string.Empty; if (str[currentPos] != '@') { return(false); } var scanner = new StringScanner(str, currentPos); // skip '@' scanner.SkipForward(1); if (!scanner.DoesMatch("fa-")) { // not a font awesome specification return(false); } scanner.SkipForward(3); iconName = string.Empty; if (!scanner.SkipIdentifier(ref iconName)) { // no icon name specified return(false); } if (string.IsNullOrWhiteSpace(iconName)) { return(false); } // matched a fontawesome specification newPos = scanner.Position; return(true); }
// Extension method. Skip an escapable character, or one normal character public static void SkipEscapableChar(this StringScanner p, bool ExtraMode) { if (p.current == '\\' && IsEscapableChar(p.CharAtOffset(1), ExtraMode)) { p.SkipForward(2); } else { p.SkipForward(1); } }
public void Tests() { var p = new StringScanner(); p.Reset("This is a string with something [bracketed]"); Assert.IsTrue(p.Bof); Assert.IsFalse(p.Eof); Assert.IsTrue(p.SkipString("This")); Assert.IsFalse(p.Bof); Assert.IsFalse(p.Eof); Assert.IsFalse(p.SkipString("huh?")); Assert.IsTrue(p.SkipLinespace()); Assert.IsTrue(p.SkipChar('i')); Assert.IsTrue(p.SkipChar('s')); Assert.IsTrue(p.SkipWhitespace()); Assert.IsTrue(p.DoesMatchAny(new char[] { 'r', 'a', 't'} )); Assert.IsFalse(p.Find("Not here")); Assert.IsFalse(p.Find("WITH")); Assert.IsFalse(p.FindI("Not here")); Assert.IsTrue(p.FindI("WITH")); Assert.IsTrue(p.Find('[')); p.SkipForward(1); p.Mark(); Assert.IsTrue(p.Find(']')); Assert.AreEqual("bracketed", p.Extract()); Assert.IsTrue(p.SkipChar(']')); Assert.IsTrue(p.Eof); }
/// <summary> /// Strips the special attributes specified at the location of start. Special attributes are a php markdown extension /// and specified between {}. Attributes are separated with spaces. It recognizes the following attributes: /// id, which start with a '#'. Only the first one is used /// css classes, which start with a'.'. All specified are used /// name=value pairs, which will end up as attributes on the element. /// </summary> /// <param name="str">The string we're scanning.</param> /// <param name="start">The start (current) position.</param> /// <param name="end">The end position. Is only valid if returned list contains at least one value.</param> /// <returns>list of special attributes found, or null if none found or error in string format.</returns> public static List <string> StripSpecialAttributes(string str, int start, out int end) { end = start; var scanner = new StringScanner(str, start); if (!scanner.DoesMatch('{')) { // not a start of a special attribute block return(null); } // first find the end of the block, scanner.SkipForward(1); var startOfAttributes = scanner.Position; // first find the next EOL, as the closing } has to be on this line. scanner.SkipToEol(); var nextEolPos = scanner.Position; scanner.Position = startOfAttributes; scanner.Mark(); if (!scanner.Find('}') || scanner.Position >= nextEolPos) { // not enclosed properly. return(null); } var attributesString = scanner.Extract(); if (string.IsNullOrWhiteSpace(attributesString)) { return(null); } // Position is on enclosing '}' (due to the Find('}'), so we have to skip 1 character end = scanner.Position + 1; return(attributesString.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s)).ToList()); }
public static string NormalizeLineEnds(string str) { if (str.IndexOfAny(lineends) < 0) { return(str); } StringBuilder sb = new StringBuilder(); StringScanner sp = new StringScanner(str); while (!sp.eof) { if (sp.eol) { sb.Append('\n'); sp.SkipEol(); } else { sb.Append(sp.current); sp.SkipForward(1); } } return(sb.ToString()); }
public static string NormalizeLineEnds(string str) { if (str.IndexOfAny(lineends) < 0) { return(str); } var sb = StringBuilderCache.Allocate(); StringScanner sp = new StringScanner(str); while (!sp.eof) { if (sp.eol) { sb.Append('\n'); sp.SkipEol(); } else { sb.Append(sp.current); sp.SkipForward(1); } } return(StringBuilderCache.Retrieve(sb)); }
/// <summary>Parse row.</summary> /// /// <param name="p">The StringScanner to process.</param> /// /// <returns>A List<string></returns> public List <string> ParseRow(StringScanner p) { p.SkipLinespace(); if (p.eol) { return(null); // Blank line ends the table } bool bAnyBars = LeadingBar; if (LeadingBar && !p.SkipChar('|')) { return(null); } // Create the row List <string> row = new List <string>(); // Parse all columns except the last while (!p.eol) { // Find the next vertical bar p.Mark(); while (!p.eol && p.current != '|') { p.SkipForward(1); } row.Add(p.Extract().Trim()); bAnyBars |= p.SkipChar('|'); } // Require at least one bar to continue the table if (!bAnyBars) { return(null); } // Add missing columns while (row.Count < Columns.Count) { row.Add(" "); } p.SkipEol(); return(row); }
public List<string> ParseRow(StringScanner p) { p.SkipLinespace(); if (p.eol) return null; // Blank line ends the table bool bAnyBars=LeadingBar; if (LeadingBar && !p.SkipChar('|')) { return null; } // Create the row List<string> row = new List<string>(); // Parse all columns except the last while (!p.eol) { // Find the next vertical bar p.Mark(); while (!p.eol && p.current != '|') p.SkipForward(1); row.Add(p.Extract().Trim()); bAnyBars|=p.SkipChar('|'); } // Require at least one bar to continue the table if (!bAnyBars) return null; // Add missing columns while (row.Count < Columns.Count) { row.Add(" "); } p.SkipEol(); return row; }
public static TableSpec ParseTableFromString(String s) { StringScanner scanner = new StringScanner(s); TableSpec spec = TableSpec.Parse(scanner); while (scanner.eof == false) { var row = spec.ParseRow(scanner); if (row != null) { spec.Rows.Add(row); } else { scanner.SkipForward(1); } } return(spec); }
// Parse just the link target // For reference link definition, this is the bit after "[id]: thisbit" // For inline link, this is the bit in the parens: [link text](thisbit) internal static LinkDefinition ParseLinkTarget(StringScanner p, string id, bool ExtraMode) { // Skip whitespace p.SkipWhitespace(); // End of string? if (p.eol) { return(null); } // Create the link definition var r = new LinkDefinition(id); // Is the url enclosed in angle brackets if (p.SkipChar('<')) { // Extract the url p.Mark(); // Find end of the url while (p.current != '>') { if (p.eof) { return(null); } p.SkipEscapableChar(ExtraMode); } string url = p.Extract(); if (!p.SkipChar('>')) { return(null); } // Unescape it r.url = Utils.UnescapeString(url.Trim(), ExtraMode); // Skip whitespace p.SkipWhitespace(); } else { // Find end of the url p.Mark(); int paren_depth = 1; while (!p.eol) { char ch = p.current; if (char.IsWhiteSpace(ch)) { break; } if (id == null) { if (ch == '(') { paren_depth++; } else if (ch == ')') { paren_depth--; if (paren_depth == 0) { break; } } } p.SkipEscapableChar(ExtraMode); } r.url = Utils.UnescapeString(p.Extract().Trim(), ExtraMode); } p.SkipLinespace(); // End of inline target if (p.DoesMatch(')')) { return(r); } bool bOnNewLine = p.eol; int posLineEnd = p.position; if (p.eol) { p.SkipEol(); p.SkipLinespace(); } // Work out what the title is delimited with char delim; switch (p.current) { case '\'': case '\"': delim = p.current; break; case '(': delim = ')'; break; default: if (bOnNewLine) { p.position = posLineEnd; return(r); } else { return(null); } } // Skip the opening title delimiter p.SkipForward(1); // Find the end of the title p.Mark(); while (true) { if (p.eol) { return(null); } if (p.current == delim) { if (delim != ')') { int savepos = p.position; // Check for embedded quotes in title // Skip the quote and any trailing whitespace p.SkipForward(1); p.SkipLinespace(); // Next we expect either the end of the line for a link definition // or the close bracket for an inline link if ((id == null && p.current != ')') || (id != null && !p.eol)) { continue; } p.position = savepos; } // End of title break; } p.SkipEscapableChar(ExtraMode); } // Store the title r.title = Utils.UnescapeString(p.Extract(), ExtraMode); // Skip closing quote p.SkipForward(1); // Done! return(r); }
private static HtmlTag ParseHelper(StringScanner p) { // Does it look like a tag? if (p.Current != '<') return null; // Skip '<' p.SkipForward(1); // Is it a comment? if (p.SkipString("!--")) { p.Mark(); if (p.Find("-->")) { var t = new HtmlTag("!"); t.m_attributes.Add("content", p.Extract()); t.m_closed = true; p.SkipForward(3); return t; } } // Is it a closing tag eg: </div> bool bClosing = p.SkipChar('/'); // Get the tag name string tagName=null; if (!p.SkipIdentifier(ref tagName)) return null; // Probably a tag, create the HtmlTag object now HtmlTag tag = new HtmlTag(tagName); tag.m_closing = bClosing; // If it's a closing tag, no attributes if (bClosing) { if (p.Current != '>') return null; p.SkipForward(1); return tag; } while (!p.Eof) { // Skip whitespace p.SkipWhitespace(); // Check for closed tag eg: <hr /> if (p.SkipString("/>")) { tag.m_closed=true; return tag; } // End of tag? if (p.SkipChar('>')) { return tag; } // attribute name string attributeName = null; if (!p.SkipIdentifier(ref attributeName)) return null; // Skip whitespace p.SkipWhitespace(); // Skip equal sign if (p.SkipChar('=')) { // Skip whitespace p.SkipWhitespace(); // Optional quotes if (p.SkipChar('\"')) { // Scan the value p.Mark(); if (!p.Find('\"')) return null; // Store the value tag.m_attributes.Add(attributeName, p.Extract()); // Skip closing quote p.SkipForward(1); } else { // Scan the value p.Mark(); while (!p.Eof && !char.IsWhiteSpace(p.Current) && p.Current != '>' && p.Current != '/') p.SkipForward(1); if (!p.Eof) { // Store the value tag.m_attributes.Add(attributeName, p.Extract()); } } } else { tag.m_attributes.Add(attributeName, ""); } } return null; }
/// <summary>Parses the given p.</summary> /// /// <param name="p">The StringScanner to process.</param> /// /// <returns>A TableSpec.</returns> public static TableSpec Parse(StringScanner p) { // Leading line space allowed p.SkipLinespace(); // Quick check for typical case if (p.current != '|' && p.current != ':' && p.current != '-') { return(null); } // Don't create the spec until it at least looks like one TableSpec spec = null; // Leading bar, looks like a table spec if (p.SkipChar('|')) { spec = new TableSpec(); spec.LeadingBar = true; } // Process all columns while (true) { // Parse column spec p.SkipLinespace(); // Must have something in the spec if (p.current == '|') { return(null); } bool AlignLeft = p.SkipChar(':'); while (p.current == '-') { p.SkipForward(1); } bool AlignRight = p.SkipChar(':'); p.SkipLinespace(); // Work out column alignment ColumnAlignment col = ColumnAlignment.NA; if (AlignLeft && AlignRight) { col = ColumnAlignment.Center; } else if (AlignLeft) { col = ColumnAlignment.Left; } else if (AlignRight) { col = ColumnAlignment.Right; } if (p.eol) { // Not a spec? if (spec == null) { return(null); } // Add the final spec? spec.Columns.Add(col); return(spec); } // We expect a vertical bar if (!p.SkipChar('|')) { return(null); } // Create the table spec if (spec == null) { spec = new TableSpec(); } // Add the column spec.Columns.Add(col); // Check for trailing vertical bar p.SkipLinespace(); if (p.eol) { spec.TrailingBar = true; return(spec); } // Next column } }
public static TableSpec Parse(StringScanner p) { // Leading line space allowed p.SkipLinespace(); // Quick check for typical case if (p.current != '|' && p.current != ':' && p.current != '-') return null; // Don't create the spec until it at least looks like one TableSpec spec = null; // Leading bar, looks like a table spec if (p.SkipChar('|')) { spec=new TableSpec(); spec.LeadingBar=true; } // Process all columns while (true) { // Parse column spec p.SkipLinespace(); // Must have something in the spec if (p.current == '|') return null; bool AlignLeft = p.SkipChar(':'); while (p.current == '-') p.SkipForward(1); bool AlignRight = p.SkipChar(':'); p.SkipLinespace(); // Work out column alignment ColumnAlignment col = ColumnAlignment.NA; if (AlignLeft && AlignRight) col = ColumnAlignment.Center; else if (AlignLeft) col = ColumnAlignment.Left; else if (AlignRight) col = ColumnAlignment.Right; if (p.eol) { // Not a spec? if (spec == null) return null; // Add the final spec? spec.Columns.Add(col); return spec; } // We expect a vertical bar if (!p.SkipChar('|')) return null; // Create the table spec if (spec==null) spec=new TableSpec(); // Add the column spec.Columns.Add(col); // Check for trailing vertical bar p.SkipLinespace(); if (p.eol) { spec.TrailingBar = true; return spec; } // Next column } }
public void TestAttacksAreBlocked(string input) { var p = new StringScanner(input); while (!p.EndOfFile) { HtmlTag tag=HtmlTag.Parse(p); if (tag!=null) { if (tag.IsSafe()) { Assert.IsTrue(IsTagReallySafe(tag)); } } else { p.SkipForward(1); } } }
private static HtmlTag ParseHelper(StringScanner p) { // Does it look like a tag? if (p.current != '<') { return(null); } // Skip '<' p.SkipForward(1); // Is it a comment? if (p.SkipString("!--")) { p.Mark(); if (p.Find("-->")) { var t = new HtmlTag("!"); t.m_attributes.Add("content", p.Extract()); t.m_closed = true; p.SkipForward(3); return(t); } } // Is it a closing tag eg: </div> bool bClosing = p.SkipChar('/'); // Get the tag name string tagName = null; if (!p.SkipIdentifier(ref tagName)) { return(null); } // Probably a tag, create the HtmlTag object now HtmlTag tag = new HtmlTag(tagName); tag.m_closing = bClosing; // If it's a closing tag, no attributes if (bClosing) { if (p.current != '>') { return(null); } p.SkipForward(1); return(tag); } while (!p.eof) { // Skip whitespace p.SkipWhitespace(); // Check for closed tag eg: <hr /> if (p.SkipString("/>")) { tag.m_closed = true; return(tag); } // End of tag? if (p.SkipChar('>')) { return(tag); } // attribute name string attributeName = null; if (!p.SkipIdentifier(ref attributeName)) { return(null); } // Skip whitespace p.SkipWhitespace(); // Skip equal sign if (p.SkipChar('=')) { // Skip whitespace p.SkipWhitespace(); // Optional quotes if (p.SkipChar('\"')) { // Scan the value p.Mark(); if (!p.Find('\"')) { return(null); } // Store the value tag.m_attributes.Add(attributeName, p.Extract()); // Skip closing quote p.SkipForward(1); } else { // Scan the value p.Mark(); while (!p.eof && !char.IsWhiteSpace(p.current) && p.current != '>' && p.current != '/') { p.SkipForward(1); } if (!p.eof) { // Store the value tag.m_attributes.Add(attributeName, p.Extract()); } } } else { tag.m_attributes.Add(attributeName, ""); } } return(null); }
public void TestAttacksAreBlocked(string input) { StringScanner p = new StringScanner(input); while (!p.Eof) { HtmlTag tag=HtmlTag.Parse(p); if (tag!=null) { if (tag.IsSafe()) { // There's a few tags that really are safe in the test data Assert.IsTrue(IsTagReallySafe(tag)); } } else { // Next character p.SkipForward(1); } } }
public void TestNonAttacksAreAllowed(string input) { StringScanner p = new StringScanner(input); while (!p.Eof) { HtmlTag tag = HtmlTag.Parse(p); if (tag != null) { Assert.IsTrue(tag.IsSafe()); } else { // Next character p.SkipForward(1); } } }
// Parse just the link target // For reference link definition, this is the bit after "[id]: thisbit" // For inline link, this is the bit in the parens: [link text](thisbit) internal static LinkDefinition ParseLinkTarget(StringScanner p, string id, bool ExtraMode) { // Skip whitespace p.SkipWhitespace(); // End of string? if (p.eol) return null; // Create the link definition var r = new LinkDefinition(id); // Is the url enclosed in angle brackets if (p.SkipChar('<')) { // Extract the url p.Mark(); // Find end of the url while (p.current != '>') { if (p.eof) return null; p.SkipEscapableChar(ExtraMode); } string url = p.Extract(); if (!p.SkipChar('>')) return null; // Unescape it r.url = Utils.UnescapeString(url.Trim(), ExtraMode); // Skip whitespace p.SkipWhitespace(); } else { // Find end of the url p.Mark(); int paren_depth = 1; while (!p.eol) { char ch=p.current; if (char.IsWhiteSpace(ch)) break; if (id == null) { if (ch == '(') paren_depth++; else if (ch == ')') { paren_depth--; if (paren_depth==0) break; } } p.SkipEscapableChar(ExtraMode); } r.url = Utils.UnescapeString(p.Extract().Trim(), ExtraMode); } p.SkipLinespace(); // End of inline target if (p.DoesMatch(')')) return r; bool bOnNewLine = p.eol; int posLineEnd = p.position; if (p.eol) { p.SkipEol(); p.SkipLinespace(); } // Work out what the title is delimited with char delim; switch (p.current) { case '\'': case '\"': delim = p.current; break; case '(': delim = ')'; break; default: if (bOnNewLine) { p.position = posLineEnd; return r; } else return null; } // Skip the opening title delimiter p.SkipForward(1); // Find the end of the title p.Mark(); while (true) { if (p.eol) return null; if (p.current == delim) { if (delim != ')') { int savepos = p.position; // Check for embedded quotes in title // Skip the quote and any trailing whitespace p.SkipForward(1); p.SkipLinespace(); // Next we expect either the end of the line for a link definition // or the close bracket for an inline link if ((id == null && p.current != ')') || (id != null && !p.eol)) { continue; } p.position = savepos; } // End of title break; } p.SkipEscapableChar(ExtraMode); } // Store the title r.title = Utils.UnescapeString(p.Extract(), ExtraMode); // Skip closing quote p.SkipForward(1); // Done! return r; }
public void TestNonAttacksAreAllowed(string input) { var p = new StringScanner(input); while (!p.EndOfFile) { var tag = HtmlTag.Parse(p); if (tag != null) { Assert.IsTrue(tag.IsSafe()); } else { p.SkipForward(1); } } }