diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js
index b3e40b33..dd858ea4 100755
--- a/src/core/config/Categories.js
+++ b/src/core/config/Categories.js
@@ -173,7 +173,6 @@ const Categories = [
"Tail",
"Count occurrences",
"Expand alphabet range",
- "Parse escaped string",
"Drop bytes",
"Take bytes",
"Pad lines",
@@ -188,6 +187,8 @@ const Categories = [
"Parse UNIX file permissions",
"Swap endianness",
"Parse colour code",
+ "Escape String",
+ "Unescape String",
]
},
{
diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js
index 38aa35cd..d6a12845 100755
--- a/src/core/config/OperationConfig.js
+++ b/src/core/config/OperationConfig.js
@@ -3224,13 +3224,6 @@ const OperationConfig = {
}
]
},
- "Parse escaped string": {
- description: "Replaces escaped characters with the bytes they represent.
e.g.Hello\\nWorld
becomes Hello
World
",
- run: StrUtils.runParseEscapedString,
- inputType: "string",
- outputType: "string",
- args: []
- },
"TCP/IP Checksum": {
description: "Calculates the checksum for a TCP (Transport Control Protocol) or IP (Internet Protocol) header from an input of raw bytes.",
run: Checksum.runTCPIP,
@@ -3270,6 +3263,20 @@ const OperationConfig = {
}
]
},
+ "Escape String": {
+ description: "Escapes a string so that it can be embedded in another. For example, Don't stop me now
becomes Don\\'t stop me now
.",
+ run: StrUtils.runEscape,
+ inputType: "string",
+ outputType: "string",
+ args: []
+ },
+ "Unescape String": {
+ description: "Unescapes a string that was embedded inside another so that it can be used in it's own right. For example, Don\\'t stop me now
becomes Don't stop me now
.",
+ run: StrUtils.runUnescape,
+ inputType: "string",
+ outputType: "string",
+ args: []
+ },
"To Morse Code": {
description: "Translates alphanumeric characters into International Morse Code.
Ignores non-Morse characters.
e.g. SOS
becomes ... --- ...
",
run: MorseCode.runTo,
diff --git a/src/core/operations/StrUtils.js b/src/core/operations/StrUtils.js
index 698e7eef..5d0c0a43 100755
--- a/src/core/operations/StrUtils.js
+++ b/src/core/operations/StrUtils.js
@@ -448,16 +448,80 @@ const StrUtils = {
return outputs.join(sampleDelim);
},
+ /**
+ * @constant
+ * @default
+ */
+ ESCAPE_REPLACEMENTS: [
+ {"escaped": "\\\\", "unescaped": "\\"}, // Must be first
+ {"escaped": "\\'", "unescaped": "'"},
+ {"escaped": "\\\"", "unescaped": "\""},
+ {"escaped": "\\n", "unescaped": "\n"},
+ {"escaped": "\\r", "unescaped": "\r"},
+ {"escaped": "\\t", "unescaped": "\t"},
+ {"escaped": "\\b", "unescaped": "\b"},
+ {"escaped": "\\f", "unescaped": "\f"},
+ ],
/**
- * Parse escaped string operation.
+ * Escapes a string for embedding in another string.
+ *
+ * @author Vel0x [dalemy@microsoft.com]
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
+ *
+ * @example
+ * StrUtils.runUnescape("Don't do that", [])
+ * > "Don\'t do that"
+ * StrUtils.runUnescape(`Hello
+ * World`, [])
+ * > "Hello\nWorld"
*/
- runParseEscapedString: function(input, args) {
- return Utils.parseEscapedChars(input);
+ runEscape: function(input, args) {
+ return StrUtils._replaceByKeys(input, "unescaped", "escaped");
+ },
+
+ /**
+ * Unescapes a string that was part of another string
+ *
+ * @author Vel0x [dalemy@microsoft.com]
+ *
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ *
+ * @example
+ * StrUtils.runUnescape("Don\'t do that", [])
+ * > "Don't do that"
+ * StrUtils.runUnescape("Hello\nWorld", [])
+ * > `Hello
+ * World`
+ */
+ runUnescape: function(input, args) {
+ return StrUtils._replaceByKeys(input, "escaped", "unescaped");
+ },
+
+ /**
+ * Replaces all matching tokens in ESCAPE_REPLACEMENTS with the correction. The
+ * ordering is determined by the pattern_key and the replacement_key.
+ *
+ * @author Vel0x [dalemy@microsoft.com]
+ * @author Matt C [matt@artemisbot.uk]
+ *
+ * @param {string} input
+ * @param {string} pattern_key
+ * @param {string} replacement_key
+ * @returns {string}
+ */
+ _replaceByKeys: function(input, patternKey, replacementKey) {
+ let output = input;
+ if (patternKey === "escaped") output = Utils.parseEscapedChars(input); // I've wrapped this to catch the \\x encoded characters
+ StrUtils.ESCAPE_REPLACEMENTS.forEach(replacement => {
+ output = output.split(replacement[patternKey]).join(replacement[replacementKey]);
+ });
+ return output;
},
diff --git a/test/tests/operations/StrUtils.js b/test/tests/operations/StrUtils.js
index 478ab1f3..159916c7 100644
--- a/test/tests/operations/StrUtils.js
+++ b/test/tests/operations/StrUtils.js
@@ -232,4 +232,48 @@ TestRegister.addTests([
}
],
},
+ {
+ name: "Escape String: quotes",
+ input: "Hello \"World\"! Escape 'these' quotes.",
+ expectedOutput: "Hello \\\"World\\\"! Escape \\'these\\' quotes.",
+ recipeConfig: [
+ {
+ "op": "Escape String",
+ "args": []
+ }
+ ],
+ },
+ {
+ name: "Escape String: special characters",
+ input: "Fizz & buzz\n\ttabbed newline\rcarriage returned line\nbackspace character: \"\" form feed character: \"\"",
+ expectedOutput: "Fizz & buzz\\n\\ttabbed newline\\rcarriage returned line\\nbackspace character: \\\"\\b\\\" form feed character: \\\"\\f\\\"",
+ recipeConfig: [
+ {
+ "op": "Escape String",
+ "args": []
+ }
+ ],
+ },
+ {
+ name: "Unescape String: quotes",
+ input: "Hello \\\"World\\\"! Escape \\'these\\' quotes.",
+ expectedOutput: "Hello \"World\"! Escape 'these' quotes.",
+ recipeConfig: [
+ {
+ "op": "Unescape String",
+ "args": []
+ }
+ ],
+ },
+ {
+ name: "Unescape String: special characters",
+ input: "Fizz \x26 buzz\\n\\ttabbed newline\\rcarriage returned line\\nbackspace character: \\\"\\b\\\" form feed character: \\\"\\f\\\"",
+ expectedOutput: "Fizz & buzz\n\ttabbed newline\rcarriage returned line\nbackspace character: \"\" form feed character: \"\"",
+ recipeConfig: [
+ {
+ "op": "Unescape String",
+ "args": []
+ }
+ ],
+ },
]);