You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					207 lines
				
				4.9 KiB
			
		
		
			
		
	
	
					207 lines
				
				4.9 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								exports.parse = exports.decode = decode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.stringify = exports.encode = encode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.safe = safe
							 | 
						||
| 
								 | 
							
								exports.unsafe = unsafe
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var eol = typeof process !== 'undefined' &&
							 | 
						||
| 
								 | 
							
								  process.platform === 'win32' ? '\r\n' : '\n'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function encode (obj, opt) {
							 | 
						||
| 
								 | 
							
								  var children = []
							 | 
						||
| 
								 | 
							
								  var out = ''
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof opt === 'string') {
							 | 
						||
| 
								 | 
							
								    opt = {
							 | 
						||
| 
								 | 
							
								      section: opt,
							 | 
						||
| 
								 | 
							
								      whitespace: false,
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    opt = opt || {}
							 | 
						||
| 
								 | 
							
								    opt.whitespace = opt.whitespace === true
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var separator = opt.whitespace ? ' = ' : '='
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Object.keys(obj).forEach(function (k, _, __) {
							 | 
						||
| 
								 | 
							
								    var val = obj[k]
							 | 
						||
| 
								 | 
							
								    if (val && Array.isArray(val)) {
							 | 
						||
| 
								 | 
							
								      val.forEach(function (item) {
							 | 
						||
| 
								 | 
							
								        out += safe(k + '[]') + separator + safe(item) + '\n'
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    } else if (val && typeof val === 'object')
							 | 
						||
| 
								 | 
							
								      children.push(k)
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      out += safe(k) + separator + safe(val) + eol
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (opt.section && out.length)
							 | 
						||
| 
								 | 
							
								    out = '[' + safe(opt.section) + ']' + eol + out
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  children.forEach(function (k, _, __) {
							 | 
						||
| 
								 | 
							
								    var nk = dotSplit(k).join('\\.')
							 | 
						||
| 
								 | 
							
								    var section = (opt.section ? opt.section + '.' : '') + nk
							 | 
						||
| 
								 | 
							
								    var child = encode(obj[k], {
							 | 
						||
| 
								 | 
							
								      section: section,
							 | 
						||
| 
								 | 
							
								      whitespace: opt.whitespace,
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								    if (out.length && child.length)
							 | 
						||
| 
								 | 
							
								      out += eol
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    out += child
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return out
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function dotSplit (str) {
							 | 
						||
| 
								 | 
							
								  return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002')
							 | 
						||
| 
								 | 
							
								    .replace(/\\\./g, '\u0001')
							 | 
						||
| 
								 | 
							
								    .split(/\./).map(function (part) {
							 | 
						||
| 
								 | 
							
								      return part.replace(/\1/g, '\\.')
							 | 
						||
| 
								 | 
							
								        .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001')
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function decode (str) {
							 | 
						||
| 
								 | 
							
								  var out = {}
							 | 
						||
| 
								 | 
							
								  var p = out
							 | 
						||
| 
								 | 
							
								  var section = null
							 | 
						||
| 
								 | 
							
								  //          section     |key      = value
							 | 
						||
| 
								 | 
							
								  var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i
							 | 
						||
| 
								 | 
							
								  var lines = str.split(/[\r\n]+/g)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  lines.forEach(function (line, _, __) {
							 | 
						||
| 
								 | 
							
								    if (!line || line.match(/^\s*[;#]/))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    var match = line.match(re)
							 | 
						||
| 
								 | 
							
								    if (!match)
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    if (match[1] !== undefined) {
							 | 
						||
| 
								 | 
							
								      section = unsafe(match[1])
							 | 
						||
| 
								 | 
							
								      if (section === '__proto__') {
							 | 
						||
| 
								 | 
							
								        // not allowed
							 | 
						||
| 
								 | 
							
								        // keep parsing the section, but don't attach it.
							 | 
						||
| 
								 | 
							
								        p = {}
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      p = out[section] = out[section] || {}
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var key = unsafe(match[2])
							 | 
						||
| 
								 | 
							
								    if (key === '__proto__')
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    var value = match[3] ? unsafe(match[4]) : true
							 | 
						||
| 
								 | 
							
								    switch (value) {
							 | 
						||
| 
								 | 
							
								      case 'true':
							 | 
						||
| 
								 | 
							
								      case 'false':
							 | 
						||
| 
								 | 
							
								      case 'null': value = JSON.parse(value)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Convert keys with '[]' suffix to an array
							 | 
						||
| 
								 | 
							
								    if (key.length > 2 && key.slice(-2) === '[]') {
							 | 
						||
| 
								 | 
							
								      key = key.substring(0, key.length - 2)
							 | 
						||
| 
								 | 
							
								      if (key === '__proto__')
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      if (!p[key])
							 | 
						||
| 
								 | 
							
								        p[key] = []
							 | 
						||
| 
								 | 
							
								      else if (!Array.isArray(p[key]))
							 | 
						||
| 
								 | 
							
								        p[key] = [p[key]]
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // safeguard against resetting a previously defined
							 | 
						||
| 
								 | 
							
								    // array by accidentally forgetting the brackets
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(p[key]))
							 | 
						||
| 
								 | 
							
								      p[key].push(value)
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      p[key] = value
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}}
							 | 
						||
| 
								 | 
							
								  // use a filter to return the keys that have to be deleted.
							 | 
						||
| 
								 | 
							
								  Object.keys(out).filter(function (k, _, __) {
							 | 
						||
| 
								 | 
							
								    if (!out[k] ||
							 | 
						||
| 
								 | 
							
								      typeof out[k] !== 'object' ||
							 | 
						||
| 
								 | 
							
								      Array.isArray(out[k]))
							 | 
						||
| 
								 | 
							
								      return false
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // see if the parent section is also an object.
							 | 
						||
| 
								 | 
							
								    // if so, add it to that, and mark this one for deletion
							 | 
						||
| 
								 | 
							
								    var parts = dotSplit(k)
							 | 
						||
| 
								 | 
							
								    var p = out
							 | 
						||
| 
								 | 
							
								    var l = parts.pop()
							 | 
						||
| 
								 | 
							
								    var nl = l.replace(/\\\./g, '.')
							 | 
						||
| 
								 | 
							
								    parts.forEach(function (part, _, __) {
							 | 
						||
| 
								 | 
							
								      if (part === '__proto__')
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      if (!p[part] || typeof p[part] !== 'object')
							 | 
						||
| 
								 | 
							
								        p[part] = {}
							 | 
						||
| 
								 | 
							
								      p = p[part]
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								    if (p === out && nl === l)
							 | 
						||
| 
								 | 
							
								      return false
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    p[nl] = out[k]
							 | 
						||
| 
								 | 
							
								    return true
							 | 
						||
| 
								 | 
							
								  }).forEach(function (del, _, __) {
							 | 
						||
| 
								 | 
							
								    delete out[del]
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return out
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isQuoted (val) {
							 | 
						||
| 
								 | 
							
								  return (val.charAt(0) === '"' && val.slice(-1) === '"') ||
							 | 
						||
| 
								 | 
							
								    (val.charAt(0) === "'" && val.slice(-1) === "'")
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function safe (val) {
							 | 
						||
| 
								 | 
							
								  return (typeof val !== 'string' ||
							 | 
						||
| 
								 | 
							
								    val.match(/[=\r\n]/) ||
							 | 
						||
| 
								 | 
							
								    val.match(/^\[/) ||
							 | 
						||
| 
								 | 
							
								    (val.length > 1 &&
							 | 
						||
| 
								 | 
							
								     isQuoted(val)) ||
							 | 
						||
| 
								 | 
							
								    val !== val.trim())
							 | 
						||
| 
								 | 
							
								    ? JSON.stringify(val)
							 | 
						||
| 
								 | 
							
								    : val.replace(/;/g, '\\;').replace(/#/g, '\\#')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function unsafe (val, doUnesc) {
							 | 
						||
| 
								 | 
							
								  val = (val || '').trim()
							 | 
						||
| 
								 | 
							
								  if (isQuoted(val)) {
							 | 
						||
| 
								 | 
							
								    // remove the single quotes before calling JSON.parse
							 | 
						||
| 
								 | 
							
								    if (val.charAt(0) === "'")
							 | 
						||
| 
								 | 
							
								      val = val.substr(1, val.length - 2)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      val = JSON.parse(val)
							 | 
						||
| 
								 | 
							
								    } catch (_) {}
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    // walk the val to find the first not-escaped ; character
							 | 
						||
| 
								 | 
							
								    var esc = false
							 | 
						||
| 
								 | 
							
								    var unesc = ''
							 | 
						||
| 
								 | 
							
								    for (var i = 0, l = val.length; i < l; i++) {
							 | 
						||
| 
								 | 
							
								      var c = val.charAt(i)
							 | 
						||
| 
								 | 
							
								      if (esc) {
							 | 
						||
| 
								 | 
							
								        if ('\\;#'.indexOf(c) !== -1)
							 | 
						||
| 
								 | 
							
								          unesc += c
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								          unesc += '\\' + c
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        esc = false
							 | 
						||
| 
								 | 
							
								      } else if (';#'.indexOf(c) !== -1)
							 | 
						||
| 
								 | 
							
								        break
							 | 
						||
| 
								 | 
							
								      else if (c === '\\')
							 | 
						||
| 
								 | 
							
								        esc = true
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        unesc += c
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (esc)
							 | 
						||
| 
								 | 
							
								      unesc += '\\'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return unesc.trim()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return val
							 | 
						||
| 
								 | 
							
								}
							 |