1 |
|
2 | exports.parse = exports.decode = decode
|
3 | exports.stringify = exports.encode = encode
|
4 |
|
5 | exports.safe = safe
|
6 | exports.unsafe = unsafe
|
7 |
|
8 | var eol = process.platform === "win32" ? "\r\n" : "\n"
|
9 |
|
10 | function encode (obj, section) {
|
11 | var children = []
|
12 | , out = ""
|
13 |
|
14 | Object.keys(obj).forEach(function (k, _, __) {
|
15 | var val = obj[k]
|
16 | if (val && typeof val === "object") {
|
17 | children.push(k)
|
18 | } else {
|
19 | out += safe(k) + " = " + safe(val) + eol
|
20 | }
|
21 | })
|
22 |
|
23 | if (section && out.length) {
|
24 | out = "[" + safe(section) + "]" + eol + out
|
25 | }
|
26 |
|
27 | children.forEach(function (k, _, __) {
|
28 | var nk = dotSplit(k).join('\\.')
|
29 | var child = encode(obj[k], (section ? section + "." : "") + nk)
|
30 | if (out.length && child.length) {
|
31 | out += eol
|
32 | }
|
33 | out += child
|
34 | })
|
35 |
|
36 | return out
|
37 | }
|
38 |
|
39 | function dotSplit (str) {
|
40 | return str.replace(/\1/g, '\2LITERAL\\1LITERAL\2')
|
41 | .replace(/\\\./g, '\1')
|
42 | .split(/\./).map(function (part) {
|
43 | return part.replace(/\1/g, '\\.')
|
44 | .replace(/\2LITERAL\\1LITERAL\2/g, '\1')
|
45 | })
|
46 | }
|
47 |
|
48 | function decode (str) {
|
49 | var out = {}
|
50 | , p = out
|
51 | , section = null
|
52 | , state = "START"
|
53 |
|
54 | , re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i
|
55 | , lines = str.split(/[\r\n]+/g)
|
56 | , section = null
|
57 |
|
58 | lines.forEach(function (line, _, __) {
|
59 |
|
60 | var rem = line.indexOf(";")
|
61 | if (rem !== -1) line = line.substr(0, rem)
|
62 | if (!line) return
|
63 | var match = line.match(re)
|
64 | if (!match) return
|
65 | if (match[1] !== undefined) {
|
66 | section = unsafe(match[1])
|
67 | p = out[section] = out[section] || {}
|
68 | return
|
69 | }
|
70 | var key = unsafe(match[2])
|
71 | , value = match[3] ? unsafe((match[4] || "")) : true
|
72 | switch (value) {
|
73 | case 'true':
|
74 | case 'false':
|
75 | case 'null': value = JSON.parse(value)
|
76 | }
|
77 | p[key] = value
|
78 | })
|
79 |
|
80 |
|
81 |
|
82 | Object.keys(out).filter(function (k, _, __) {
|
83 | if (!out[k] || typeof out[k] !== "object") return false
|
84 |
|
85 |
|
86 | var parts = dotSplit(k)
|
87 | , p = out
|
88 | , l = parts.pop()
|
89 | , nl = l.replace(/\\\./g, '.')
|
90 | parts.forEach(function (part, _, __) {
|
91 | if (!p[part] || typeof p[part] !== "object") p[part] = {}
|
92 | p = p[part]
|
93 | })
|
94 | if (p === out && nl === l) return false
|
95 | p[nl] = out[k]
|
96 | return true
|
97 | }).forEach(function (del, _, __) {
|
98 | delete out[del]
|
99 | })
|
100 |
|
101 | return out
|
102 | }
|
103 |
|
104 | function safe (val) {
|
105 | return ( typeof val !== "string"
|
106 | || val.match(/[\r\n]/)
|
107 | || val.match(/^\[/)
|
108 | || (val.length > 1
|
109 | && val.charAt(0) === "\""
|
110 | && val.slice(-1) === "\"")
|
111 | || val !== val.trim() ) ? JSON.stringify(val) : val
|
112 | }
|
113 |
|
114 | function unsafe (val) {
|
115 | val = (val || "").trim()
|
116 | if (val.charAt(0) === "\"" && val.slice(-1) === "\"") {
|
117 | try { val = JSON.parse(val) } catch (_) {}
|
118 | }
|
119 | return val
|
120 | }
|