diff --git a/lib/ini.js b/lib/ini.js index beb390d..100a04f 100644 --- a/lib/ini.js +++ b/lib/ini.js @@ -57,7 +57,7 @@ const encode = (obj, opt = {}) => { } if (opt.section && out.length) { - out = '[' + safe(opt.section) + ']' + (opt.newline ? eol + eol : eol) + out + out = '[' + safe(opt.section, false) + ']' + (opt.newline ? eol + eol : eol) + out } for (const k of children) { @@ -214,7 +214,7 @@ const isQuoted = val => { (val.startsWith("'") && val.endsWith("'")) } -const safe = val => { +const safe = (val, escapeBackslash = true) => { if ( typeof val !== 'string' || val.match(/[=\r\n]/) || @@ -224,7 +224,11 @@ const safe = val => { ) { return JSON.stringify(val) } - return val.split(';').join('\\;').split('#').join('\\#') + // `unsafe` treats `\` as an escape character and unescapes `\\`, `\;` and + // `\#`, so values and keys must escape backslashes to round-trip. Section + // names opt out because they use `\.` to escape literal dots in key paths. + const escaped = escapeBackslash ? val.split('\\').join('\\\\') : val + return escaped.split(';').join('\\;').split('#').join('\\#') } const unsafe = val => { diff --git a/test/bar.js b/test/bar.js index 8f7eb23..9669407 100644 --- a/test/bar.js +++ b/test/bar.js @@ -19,3 +19,25 @@ test('parse(stringify(x)) is same as x', function (t) { t.end() }) + +// `unsafe` unescapes `\\`, `\;` and `\#`, so `safe` has to escape the +// backslash for those values to survive a round trip. +test('parse(stringify(x)) round-trips values containing backslashes', function (t) { + var values = { + 'two backslashes': '\\\\', // \ \ + 'windows path': 'C:\\\\tmp\\\\x', // C : \ \ t m p \ \ x + 'backslash before semicolon': 'a\\;b', // a \ ; b + 'backslash before hash': 'a\\#b', // a \ # b + } + for (var label in values) { + var obj = { k: values[label] } + t.same(ini.parse(ini.stringify(obj)), obj, label) + } + + // a backslash in a key name has to round-trip as well + var keyed = {} + keyed['a\\\\b'] = 'v' // a \ \ b + t.same(ini.parse(ini.stringify(keyed)), keyed, 'backslash in key') + + t.end() +})