From fadd7158ed4cd79c1a3800ff1a4973bee8a4f4c7 Mon Sep 17 00:00:00 2001 From: Dale Myers Date: Mon, 23 Jan 2017 21:39:08 +0000 Subject: [PATCH 1/3] Add string escape/unescape operations These operations are useful for taking the contents of a string, and making it suitable for use as a stand alone string. For example, in an IDE you might see a string which is represented as: "Say \"Hello\"". The escaped double quotes are shown to make it clear that they do not end the string, despite the fact that they are not truly part of the string. In order to get the raw string, you would need to copy this, then manually remove the backslashes. The new String_.run_unescape operation does this automatically. The String_.run_escape is the inverse. It allows you to take a string like the one above, and paste it between two quotes without having to manually escape it. --- src/js/config/Categories.js | 2 + src/js/config/OperationConfig.js | 16 +++++++- src/js/operations/String.js | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100755 src/js/operations/String.js diff --git a/src/js/config/Categories.js b/src/js/config/Categories.js index db43721c..d7ad1407 100755 --- a/src/js/config/Categories.js +++ b/src/js/config/Categories.js @@ -167,6 +167,8 @@ var Categories = [ "Parse UNIX file permissions", "Swap endianness", "Parse colour code", + "Escape String", + "Unescape String", ] }, { diff --git a/src/js/config/OperationConfig.js b/src/js/config/OperationConfig.js index b5ea0427..cbed494f 100755 --- a/src/js/config/OperationConfig.js +++ b/src/js/config/OperationConfig.js @@ -2952,5 +2952,19 @@ var OperationConfig = { value: Cipher.SUBS_CIPHERTEXT } ] - } + }, + "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: String_.run_escape, + input_type: "string", + output_type: "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: String_.run_unescape, + input_type: "string", + output_type: "string", + args: [] + }, }; diff --git a/src/js/operations/String.js b/src/js/operations/String.js new file mode 100755 index 00000000..a85b385b --- /dev/null +++ b/src/js/operations/String.js @@ -0,0 +1,67 @@ +/** + * String operations. + * Namespace is appended with an underscore to prevent overwriting the global String object. + * + * @author Vel0x + * @namespace + */ +var String_ = { + + /** + * @constant + * @default + */ + REPLACEMENTS: [ + {"escaped": "\\\\", "unescaped":"\\"}, // Must be first + {"escaped": "\\'", "unescaped":"'"}, + {"escaped": "\\\"", "unescaped":"\""}, + {"escaped": "\\n", "unescaped":"\n"}, + {"escaped": "\\r", "unescaped":"\r"}, + ], + + /** + * Escapes a string for embedding in another string. + * + * Example: "Don't do that" -> "Don\'t do that" + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run_escape: function(input, args) { + return String_._replace_by_keys(input, "unescaped", "escaped") + }, + + /** + * Unescapes a string that was part of another string + * + * Example: "Don\'t do that" -> "Don't do that" + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run_unescape: function(input, args) { + return String_._replace_by_keys(input, "escaped", "unescaped") + }, + + /** + * Replaces all matching tokens in REPLACEMENTS with the correction. The + * ordering is determined by the pattern_key and the replacement_key. + * + * @param {string} input + * @param {string} pattern_key + * @param {string} replacement_key + * @returns {string} + */ + _replace_by_keys: function(input, pattern_key, replacement_key) { + var output = input; + var replacementsLength = String_.REPLACEMENTS.length; + for (var i = 0; i < replacementsLength; i++) { + var replacement = String_.REPLACEMENTS[i]; + output = output.split(replacement[pattern_key]).join(replacement[replacement_key]); + } + return output + }, + +}; From 9161cc693d420f99213bf3e256eaf10183a9f2ef Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 4 Aug 2017 15:54:00 +0100 Subject: [PATCH 2/3] Removes need for runParseEscapedString - Fixes examples - Actually makes it work --- src/core/config/OperationConfig.js | 4 +-- src/core/operations/StrUtils.js | 40 ++++++++++++++---------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index 645b7629..417dea3a 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -3254,14 +3254,14 @@ 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.run_escape, + 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.run_unescape, + run: StrUtils.runUnescape, inputType: "string", outputType: "string", args: [] diff --git a/src/core/operations/StrUtils.js b/src/core/operations/StrUtils.js index 9250620f..58b7e7a7 100755 --- a/src/core/operations/StrUtils.js +++ b/src/core/operations/StrUtils.js @@ -448,18 +448,6 @@ const StrUtils = { return outputs.join(sampleDelim); }, - - /** - * Parse escaped string operation. - * - * @param {string} input - * @param {Object[]} args - * @returns {string} - */ - runParseEscapedString: function(input, args) { - return Utils.parseEscapedChars(input); - }, - /** * @constant * @default @@ -475,31 +463,41 @@ const StrUtils = { /** * Escapes a string for embedding in another string. * - * Example: "Don't do that" -> "Don\'t do that" - * * @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" */ runEscape: function(input, args) { - return StrUtils._replace_by_keys(input, "unescaped", "escaped"); + return StrUtils._replaceByKeys(input, "unescaped", "escaped"); }, /** * Unescapes a string that was part of another string * - * Example: "Don\'t do that" -> "Don't do that" - * * @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._replace_by_keys(input, "escaped", "unescaped"); + return StrUtils._replaceByKeys(Utils.parseEscapedChars(input), "escaped", "unescaped"); }, /** @@ -515,12 +513,10 @@ const StrUtils = { * @returns {string} */ _replaceByKeys: function(input, patternKey, replacementKey) { - const replacementsLength = StrUtils.ESCAPE_REPLACEMENTS.length; let output = input; - for (let i = 0; i < replacementsLength; i++) { - const replacement = StrUtils.ESCAPE_REPLACEMENTS[i]; + StrUtils.ESCAPE_REPLACEMENTS.forEach(replacement => { output = output.split(replacement[patternKey]).join(replacement[replacementKey]); - } + }); return output; }, From 6698a2ac1347bf7f918314449f39b884daf1588e Mon Sep 17 00:00:00 2001 From: Matt C Date: Mon, 7 Aug 2017 16:08:50 +0100 Subject: [PATCH 3/3] Added tests + fixes for PR - actually removed prev func - shuffled some stuff around --- src/core/config/Categories.js | 1 - src/core/config/OperationConfig.js | 7 ----- src/core/operations/StrUtils.js | 6 +++- test/tests/operations/StrUtils.js | 44 ++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 7599aaca..f5c056c3 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", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index 417dea3a..fb076bd1 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -3206,13 +3206,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, diff --git a/src/core/operations/StrUtils.js b/src/core/operations/StrUtils.js index 58b7e7a7..5d0c0a43 100755 --- a/src/core/operations/StrUtils.js +++ b/src/core/operations/StrUtils.js @@ -458,6 +458,9 @@ const StrUtils = { {"escaped": "\\\"", "unescaped": "\""}, {"escaped": "\\n", "unescaped": "\n"}, {"escaped": "\\r", "unescaped": "\r"}, + {"escaped": "\\t", "unescaped": "\t"}, + {"escaped": "\\b", "unescaped": "\b"}, + {"escaped": "\\f", "unescaped": "\f"}, ], /** @@ -497,7 +500,7 @@ const StrUtils = { * World` */ runUnescape: function(input, args) { - return StrUtils._replaceByKeys(Utils.parseEscapedChars(input), "escaped", "unescaped"); + return StrUtils._replaceByKeys(input, "escaped", "unescaped"); }, /** @@ -514,6 +517,7 @@ const StrUtils = { */ _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]); }); 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": [] + } + ], + }, ]);