/// <summary> /// Escape string with backslash sequences (e.g. \r\n -> \\r\\n) /// </summary> /// <param name="s">String to escape</param> /// <param name="stringEscapeOptions">String escape options</param> /// <param name="charEscapeOptions">Char escape options</param> /// <param name="addQuotes">Add quotes after escaping</param> /// <returns>String with escape sequences for string</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> public static string Escape(string s, StringEscapeOptions stringEscapeOptions = null, CharEscapeOptions charEscapeOptions = null, bool addQuotes = false) { if (s == null) { throw new ArgumentNullException(nameof(s)); } if (stringEscapeOptions == null) { stringEscapeOptions = new StringEscapeOptions(); } if (charEscapeOptions == null) { charEscapeOptions = new CharEscapeOptions(); } switch (charEscapeOptions.EscapeLanguage) { case CharEscapeLanguage.CSharp: return(EscapeCSharp(s, stringEscapeOptions, charEscapeOptions, addQuotes)); case CharEscapeLanguage.FSharp: return(EscapeFSharp(s, stringEscapeOptions, charEscapeOptions, addQuotes)); case CharEscapeLanguage.PowerShell: return(EscapePowerShell(s, stringEscapeOptions, charEscapeOptions, addQuotes)); default: throw new ArgumentException(string.Format("{0} is not a valid {1}.", charEscapeOptions.EscapeLanguage, nameof(charEscapeOptions.EscapeLanguage)), nameof(charEscapeOptions)); } }
/// <summary> /// Escape char with backslash sequence (e.g. \n -> \\n) /// </summary> /// <param name="c">Char to escape</param> /// <param name="escapeOptions">Escape options</param> /// <param name="addQuotes">Add quotes after escaping</param> /// <returns>String with escape sequence for char</returns> /// <exception cref="ArgumentException"></exception> public static string Escape(char c, CharEscapeOptions escapeOptions = null, bool addQuotes = false) { if (escapeOptions == null) { escapeOptions = new CharEscapeOptions(); } switch (escapeOptions.EscapeLanguage) { case CharEscapeLanguage.CSharp: return(EscapeCSharp(c, escapeOptions, addQuotes)); case CharEscapeLanguage.FSharp: return(EscapeFSharp(c, escapeOptions, addQuotes)); case CharEscapeLanguage.PowerShell: throw new ArgumentException(string.Format("{0} does not have a char type.", escapeOptions.EscapeLanguage), nameof(escapeOptions)); default: throw new ArgumentException(string.Format("{0} is not a valid {1}.", escapeOptions.EscapeLanguage, nameof(escapeOptions.EscapeLanguage)), nameof(escapeOptions)); } }
/// <summary> /// Escape char with backslash sequence (e.g. \n -> \\n) /// </summary> /// <param name="c">Char to escape</param> /// <param name="escapeOptions">Escape options</param> /// <param name="addQuotes">Add quotes after escaping</param> /// <returns>String with escape sequence for char</returns> /// <exception cref="ArgumentException"></exception> internal static string EscapeCSharp(char c, CharEscapeOptions escapeOptions, bool addQuotes = false) { if (escapeOptions.UseShortEscape) { switch (c) { case '\'': return("\\\'"); case '\"': return("\\\""); case '\\': return("\\\\"); case '\0': return("\\0"); case '\a': return("\\a"); case '\b': return("\\b"); case '\f': return("\\f"); case '\n': return("\\n"); case '\r': return("\\r"); case '\t': return("\\t"); case '\v': return("\\v"); } } string xu; string hex; switch (escapeOptions.EscapeLetter) { case CharEscapeLetter.LowerCaseX1: xu = "x"; hex = escapeOptions.UseLowerCaseHex ? "x1" : "X1"; break; case CharEscapeLetter.LowerCaseX2: xu = "x"; hex = escapeOptions.UseLowerCaseHex ? "x2" : "X2"; break; case CharEscapeLetter.LowerCaseX3: xu = "x"; hex = escapeOptions.UseLowerCaseHex ? "x3" : "X3"; break; case CharEscapeLetter.LowerCaseX4: xu = "x"; hex = escapeOptions.UseLowerCaseHex ? "x4" : "X4"; break; case CharEscapeLetter.LowerCaseU4: xu = "u"; hex = escapeOptions.UseLowerCaseHex ? "x4" : "X4"; break; case CharEscapeLetter.UpperCaseU8: xu = "U"; hex = escapeOptions.UseLowerCaseHex ? "x8" : "X8"; break; default: throw new ArgumentException(string.Format("{0} is not a valid {1} for {2}.", escapeOptions.EscapeLetter, nameof(escapeOptions.EscapeLetter), escapeOptions.EscapeLanguage), nameof(escapeOptions)); } string escaped = "\\" + xu + ((int)c).ToString(hex); if (addQuotes) { escaped = "\'" + escaped + "\'"; } return(escaped); }
/// <summary> /// Escape surrogate pair with `u{HHHHH} /// </summary> /// <param name="highSurrogate">High surrogate</param> /// <param name="lowSurrogate">Low surrogate</param> /// <param name="escapeOptions">Char escape options</param> /// <returns>String with escape sequence for surrogate pair</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentException"></exception> internal static string EscapeSurrogatePairPowerShell(char highSurrogate, char lowSurrogate, CharEscapeOptions escapeOptions) { int codePoint = char.ConvertToUtf32(highSurrogate, lowSurrogate); string hex; switch (escapeOptions.SurrogatePairEscapeLetter) { // 1/2/3/4 will output as 5 so we can just fall through case CharEscapeLetter.LowerCaseU1: case CharEscapeLetter.LowerCaseU2: case CharEscapeLetter.LowerCaseU3: case CharEscapeLetter.LowerCaseU4: case CharEscapeLetter.LowerCaseU5: hex = escapeOptions.UseLowerCaseHex ? "x5" : "X5"; break; case CharEscapeLetter.LowerCaseU6: hex = escapeOptions.UseLowerCaseHex ? "x6" : "X6"; break; default: throw new ArgumentException(string.Format("{0} is not a valid {1} for {2}.", escapeOptions.SurrogatePairEscapeLetter, nameof(escapeOptions.SurrogatePairEscapeLetter), escapeOptions.EscapeLanguage), nameof(escapeOptions)); } return("`u{" + codePoint.ToString(hex) + "}"); }
/// <summary> /// Escape surrogate pair with \\UHHHHHHHH /// </summary> /// <param name="highSurrogate">High surrogate</param> /// <param name="lowSurrogate">Low surrogate</param> /// <param name="escapeOptions">Char escape options</param> /// <returns>String with escape sequence for surrogate pair</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentException"></exception> internal static string EscapeSurrogatePairFSharp(char highSurrogate, char lowSurrogate, CharEscapeOptions escapeOptions) { int codePoint = char.ConvertToUtf32(highSurrogate, lowSurrogate); string hex; switch (escapeOptions.SurrogatePairEscapeLetter) { case CharEscapeLetter.UpperCaseU8: hex = escapeOptions.UseLowerCaseHex ? "x8" : "X8"; break; default: throw new ArgumentException(string.Format("{0} is not a valid {1} for {2}.", escapeOptions.SurrogatePairEscapeLetter, nameof(escapeOptions.SurrogatePairEscapeLetter), escapeOptions.EscapeLanguage), nameof(escapeOptions)); } return("\\U" + codePoint.ToString(hex)); }
/// <summary> /// Escape char with backslash sequence (e.g. \n -> `n) /// </summary> /// <param name="c">Char to escape</param> /// <param name="escapeOptions">Escape options</param> /// <returns>String with escape sequence for char</returns> /// <exception cref="ArgumentException"></exception> internal static string EscapePowerShell(char c, CharEscapeOptions escapeOptions) { if (escapeOptions.UseShortEscape) { // escaping single quotes here doesn't make any sense // within double-quoted strings `' is the same as ' // within single-quoted strings `' doesn't work, you have to double ' to '' switch (c) { case '\0': return("`0"); case '\a': return("`a"); case '\b': return("`b"); case '\x1B': // escape return("`e"); // this is supported in PowerShell v7, but not Windows PowerShell v5 case '\f': return("`f"); case '\n': return("`n"); case '\r': return("`r"); case '\t': return("`t"); case '\v': return("`v"); case '`': return("``"); case '\"': return("`\""); // technically you can use either `" or "" within double-quoted strings case '$': return("`$"); // dollar sign interpolates variables, something we can't support, so escape it } } string hex; switch (escapeOptions.EscapeLetter) { case CharEscapeLetter.LowerCaseU1: hex = escapeOptions.UseLowerCaseHex ? "x1" : "X1"; break; case CharEscapeLetter.LowerCaseU2: hex = escapeOptions.UseLowerCaseHex ? "x2" : "X2"; break; case CharEscapeLetter.LowerCaseU3: hex = escapeOptions.UseLowerCaseHex ? "x3" : "X3"; break; case CharEscapeLetter.LowerCaseU4: hex = escapeOptions.UseLowerCaseHex ? "x4" : "X4"; break; case CharEscapeLetter.LowerCaseU5: hex = escapeOptions.UseLowerCaseHex ? "x5" : "X5"; break; case CharEscapeLetter.LowerCaseU6: hex = escapeOptions.UseLowerCaseHex ? "x6" : "X6"; break; default: throw new ArgumentException(string.Format("{0} is not a valid {1} for {2}.", escapeOptions.EscapeLetter, nameof(escapeOptions.EscapeLetter), escapeOptions.EscapeLanguage), nameof(escapeOptions)); } return("`u{" + ((int)c).ToString(hex) + "}"); // this is supported in PowerShell v7, but not Windows PowerShell v5 }
/// <summary> /// Escape char with backslash sequence (e.g. \n -> \\n) /// </summary> /// <param name="c">Char to escape</param> /// <param name="escapeOptions">Escape options</param> /// <param name="addQuotes">Add quotes after escaping</param> /// <returns>String with escape sequence for char</returns> /// <exception cref="ArgumentException"></exception> internal static string EscapeFSharp(char c, CharEscapeOptions escapeOptions, bool addQuotes = false) { if (escapeOptions.UseShortEscape) { // FSharp doesn't define \0, the closest is \000 switch (c) { case '\a': return("\\a"); case '\b': return("\\b"); case '\f': return("\\f"); case '\n': return("\\n"); case '\r': return("\\r"); case '\t': return("\\t"); case '\v': return("\\v"); case '\\': return("\\\\"); case '\'': return("\\\'"); case '\"': return("\\\""); } } string xu = null; string hex = null; switch (escapeOptions.EscapeLetter) { case CharEscapeLetter.None3: if (c <= 255) { xu = ""; hex = "D3"; } break; case CharEscapeLetter.LowerCaseX2: if (c <= 0xFF) { xu = "x"; hex = escapeOptions.UseLowerCaseHex ? "x2" : "X2"; } break; case CharEscapeLetter.LowerCaseU4: xu = "u"; hex = escapeOptions.UseLowerCaseHex ? "x4" : "X4"; break; case CharEscapeLetter.UpperCaseU8: xu = "U"; hex = escapeOptions.UseLowerCaseHex ? "x8" : "X8"; break; default: throw new ArgumentException(string.Format("{0} is not a valid {1} for {2}.", escapeOptions.EscapeLetter, nameof(escapeOptions.EscapeLetter), escapeOptions.EscapeLanguage), nameof(escapeOptions)); } if (xu == null || hex == null) { switch (escapeOptions.EscapeLetterFallback) { case CharEscapeLetter.LowerCaseU4: xu = "u"; hex = escapeOptions.UseLowerCaseHex ? "x4" : "X4"; break; case CharEscapeLetter.UpperCaseU8: xu = "U"; hex = escapeOptions.UseLowerCaseHex ? "x8" : "X8"; break; default: throw new ArgumentException(string.Format("{0} is not a valid {1} for {2}.", escapeOptions.EscapeLetterFallback, nameof(escapeOptions.EscapeLetterFallback), escapeOptions.EscapeLanguage), nameof(escapeOptions)); } } string escaped = "\\" + xu + ((int)c).ToString(hex); if (addQuotes) { escaped = "\'" + escaped + "\'"; } return(escaped); }
/// <summary> /// Escape string with backslash sequences (e.g. \r\n -> \\r\\n) /// </summary> /// <param name="s">String to escape</param> /// <param name="stringEscapeOptions">String escape options</param> /// <param name="charEscapeOptions"></param> /// <param name="addQuotes">Add quotes after escaping</param> /// <returns>String with escape sequences for string</returns> /// <exception cref="ArgumentException"></exception> private static string EscapeCSharp(string s, StringEscapeOptions stringEscapeOptions, CharEscapeOptions charEscapeOptions, bool addQuotes) { CharEscapeOptions charEscapeOptionsLowerCaseX4 = new CharEscapeOptions( escapeLanguage: charEscapeOptions.EscapeLanguage, escapeLetter: CharEscapeLetter.LowerCaseX4, escapeLetterFallback: charEscapeOptions.EscapeLetterFallback, surrogatePairEscapeLetter: charEscapeOptions.SurrogatePairEscapeLetter, useLowerCaseHex: charEscapeOptions.UseLowerCaseHex, useShortEscape: charEscapeOptions.UseShortEscape ); StringBuilder sb = new StringBuilder(s.Length); for (int i = 0; i < s.Length; i++) { if (stringEscapeOptions.EscapeSurrogatePairs && i + 1 < s.Length && char.IsSurrogatePair(s[i], s[i + 1])) { sb.Append(CharUtils.EscapeSurrogatePairCSharp(s[i], s[++i], charEscapeOptions)); } else { switch (stringEscapeOptions.EscapeKind) { case StringEscapeKind.EscapeAll: sb.Append(CharUtils.EscapeCSharp(s[i], charEscapeOptions)); break; case StringEscapeKind.EscapeNonAscii: if (s[i].IsPrintAscii() && !s[i].IsBackslash() && !s[i].IsDoubleQuote()) { sb.Append(s[i]); } else { // pay special attention here because \x is variable length: H, HH, HHH, HHHH // if the next char is hex then we don't want to insert it in any of the 'H' spaces // instead we have to output the full fixed length \xHHHH so the next char doesn't become part of this \x sequence if ((charEscapeOptions.EscapeLetter == CharEscapeLetter.LowerCaseX1 || charEscapeOptions.EscapeLetter == CharEscapeLetter.LowerCaseX2 || charEscapeOptions.EscapeLetter == CharEscapeLetter.LowerCaseX3) && i + 1 < s.Length && s[i + 1].IsHex()) { sb.Append(CharUtils.EscapeCSharp(s[i], charEscapeOptionsLowerCaseX4)); } else { sb.Append(CharUtils.EscapeCSharp(s[i], charEscapeOptions)); } } break; default: throw new ArgumentException(string.Format("{0} is not a valid {1}.", stringEscapeOptions.EscapeKind, nameof(stringEscapeOptions.EscapeKind)), nameof(stringEscapeOptions)); } } } if (addQuotes) { sb.Insert(0, '\"'); sb.Append('\"'); } return(sb.ToString()); }
/// <summary> /// Escape string with backslash sequences (e.g. \r\n -> `r`n) /// </summary> /// <param name="s">String to escape</param> /// <param name="stringEscapeOptions">String escape options</param> /// <param name="charEscapeOptions">Char escape options</param> /// <param name="addQuotes">Add quotes after escaping</param> /// <returns>String with escape sequences for string</returns> /// <exception cref="ArgumentException"></exception> private static string EscapePowerShell(string s, StringEscapeOptions stringEscapeOptions, CharEscapeOptions charEscapeOptions, bool addQuotes) { StringBuilder sb = new StringBuilder(s.Length); for (int i = 0; i < s.Length; i++) { if (stringEscapeOptions.EscapeSurrogatePairs && i + 1 < s.Length && char.IsSurrogatePair(s[i], s[i + 1])) { sb.Append(CharUtils.EscapeSurrogatePairPowerShell(s[i], s[++i], charEscapeOptions)); } else { switch (stringEscapeOptions.EscapeKind) { case StringEscapeKind.EscapeAll: sb.Append(CharUtils.EscapePowerShell(s[i], charEscapeOptions)); break; case StringEscapeKind.EscapeNonAscii: if (s[i].IsPrintAscii() && !s[i].IsBacktick() && !s[i].IsDoubleQuote() && !s[i].IsDollarSign()) { sb.Append(s[i]); } else { sb.Append(CharUtils.EscapePowerShell(s[i], charEscapeOptions)); } break; default: throw new ArgumentException(string.Format("{0} is not a valid {1}.", stringEscapeOptions.EscapeKind, nameof(stringEscapeOptions.EscapeKind)), nameof(stringEscapeOptions)); } } } if (addQuotes) { sb.Insert(0, '\"'); sb.Append('\"'); } return(sb.ToString()); }