Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lib/ini.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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]/) ||
Expand All @@ -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 => {
Expand Down
22 changes: 22 additions & 0 deletions test/bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})