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.

ini.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. exports.parse = exports.decode = decode
  2. exports.stringify = exports.encode = encode
  3. exports.safe = safe
  4. exports.unsafe = unsafe
  5. var eol = typeof process !== 'undefined' &&
  6. process.platform === 'win32' ? '\r\n' : '\n'
  7. function encode (obj, opt) {
  8. var children = []
  9. var out = ''
  10. if (typeof opt === 'string') {
  11. opt = {
  12. section: opt,
  13. whitespace: false
  14. }
  15. } else {
  16. opt = opt || {}
  17. opt.whitespace = opt.whitespace === true
  18. }
  19. var separator = opt.whitespace ? ' = ' : '='
  20. Object.keys(obj).forEach(function (k, _, __) {
  21. var val = obj[k]
  22. if (val && Array.isArray(val)) {
  23. val.forEach(function (item) {
  24. out += safe(k + '[]') + separator + safe(item) + '\n'
  25. })
  26. } else if (val && typeof val === 'object') {
  27. children.push(k)
  28. } else {
  29. out += safe(k) + separator + safe(val) + eol
  30. }
  31. })
  32. if (opt.section && out.length) {
  33. out = '[' + safe(opt.section) + ']' + eol + out
  34. }
  35. children.forEach(function (k, _, __) {
  36. var nk = dotSplit(k).join('\\.')
  37. var section = (opt.section ? opt.section + '.' : '') + nk
  38. var child = encode(obj[k], {
  39. section: section,
  40. whitespace: opt.whitespace
  41. })
  42. if (out.length && child.length) {
  43. out += eol
  44. }
  45. out += child
  46. })
  47. return out
  48. }
  49. function dotSplit (str) {
  50. return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002')
  51. .replace(/\\\./g, '\u0001')
  52. .split(/\./).map(function (part) {
  53. return part.replace(/\1/g, '\\.')
  54. .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001')
  55. })
  56. }
  57. function decode (str) {
  58. var out = {}
  59. var p = out
  60. var section = null
  61. // section |key = value
  62. var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i
  63. var lines = str.split(/[\r\n]+/g)
  64. lines.forEach(function (line, _, __) {
  65. if (!line || line.match(/^\s*[;#]/)) return
  66. var match = line.match(re)
  67. if (!match) return
  68. if (match[1] !== undefined) {
  69. section = unsafe(match[1])
  70. p = out[section] = out[section] || {}
  71. return
  72. }
  73. var key = unsafe(match[2])
  74. var value = match[3] ? unsafe(match[4]) : true
  75. switch (value) {
  76. case 'true':
  77. case 'false':
  78. case 'null': value = JSON.parse(value)
  79. }
  80. // Convert keys with '[]' suffix to an array
  81. if (key.length > 2 && key.slice(-2) === '[]') {
  82. key = key.substring(0, key.length - 2)
  83. if (!p[key]) {
  84. p[key] = []
  85. } else if (!Array.isArray(p[key])) {
  86. p[key] = [p[key]]
  87. }
  88. }
  89. // safeguard against resetting a previously defined
  90. // array by accidentally forgetting the brackets
  91. if (Array.isArray(p[key])) {
  92. p[key].push(value)
  93. } else {
  94. p[key] = value
  95. }
  96. })
  97. // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}}
  98. // use a filter to return the keys that have to be deleted.
  99. Object.keys(out).filter(function (k, _, __) {
  100. if (!out[k] ||
  101. typeof out[k] !== 'object' ||
  102. Array.isArray(out[k])) {
  103. return false
  104. }
  105. // see if the parent section is also an object.
  106. // if so, add it to that, and mark this one for deletion
  107. var parts = dotSplit(k)
  108. var p = out
  109. var l = parts.pop()
  110. var nl = l.replace(/\\\./g, '.')
  111. parts.forEach(function (part, _, __) {
  112. if (!p[part] || typeof p[part] !== 'object') p[part] = {}
  113. p = p[part]
  114. })
  115. if (p === out && nl === l) {
  116. return false
  117. }
  118. p[nl] = out[k]
  119. return true
  120. }).forEach(function (del, _, __) {
  121. delete out[del]
  122. })
  123. return out
  124. }
  125. function isQuoted (val) {
  126. return (val.charAt(0) === '"' && val.slice(-1) === '"') ||
  127. (val.charAt(0) === "'" && val.slice(-1) === "'")
  128. }
  129. function safe (val) {
  130. return (typeof val !== 'string' ||
  131. val.match(/[=\r\n]/) ||
  132. val.match(/^\[/) ||
  133. (val.length > 1 &&
  134. isQuoted(val)) ||
  135. val !== val.trim())
  136. ? JSON.stringify(val)
  137. : val.replace(/;/g, '\\;').replace(/#/g, '\\#')
  138. }
  139. function unsafe (val, doUnesc) {
  140. val = (val || '').trim()
  141. if (isQuoted(val)) {
  142. // remove the single quotes before calling JSON.parse
  143. if (val.charAt(0) === "'") {
  144. val = val.substr(1, val.length - 2)
  145. }
  146. try { val = JSON.parse(val) } catch (_) {}
  147. } else {
  148. // walk the val to find the first not-escaped ; character
  149. var esc = false
  150. var unesc = ''
  151. for (var i = 0, l = val.length; i < l; i++) {
  152. var c = val.charAt(i)
  153. if (esc) {
  154. if ('\\;#'.indexOf(c) !== -1) {
  155. unesc += c
  156. } else {
  157. unesc += '\\' + c
  158. }
  159. esc = false
  160. } else if (';#'.indexOf(c) !== -1) {
  161. break
  162. } else if (c === '\\') {
  163. esc = true
  164. } else {
  165. unesc += c
  166. }
  167. }
  168. if (esc) {
  169. unesc += '\\'
  170. }
  171. return unesc.trim()
  172. }
  173. return val
  174. }