pax_global_header00006660000000000000000000000064120776417320014523gustar00rootroot0000000000000052 comment=3f0fada0f32aac5bb20c5925260f9bbfa6dc8604 ini-1.1.0/000077500000000000000000000000001207764173200123015ustar00rootroot00000000000000ini-1.1.0/LICENSE000066400000000000000000000021041207764173200133030ustar00rootroot00000000000000Copyright 2009, 2010, 2011 Isaac Z. Schlueter. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ini-1.1.0/README.md000066400000000000000000000041651207764173200135660ustar00rootroot00000000000000An ini format parser and serializer for node. Sections are treated as nested objects. Items before the first heading are saved on the object directly. ## Usage Consider an ini-file `config.ini` that looks like this: ; this comment is being ignored scope = global [database] user = dbuser password = dbpassword database = use_this_database [paths.default] datadir = /var/lib/data array[] = first value array[] = second value array[] = third value You can read, manipulate and write the ini-file like so: var fs = require('fs') , ini = require('ini') var config = ini.parse(fs.readFileSync('./config.ini', 'utf-8')) config.scope = 'local' config.database.database = 'use_another_database' config.paths.default.tmpdir = '/tmp' delete config.paths.default.datadir config.paths.default.array.push('fourth value') fs.writeFileSync('./config_modified.ini', ini.stringify(config, 'section')) This will result in a file called `config_modified.ini` being written to the filesystem with the following content: [section] scope = local [section.database] user = dbuser password = dbpassword database = use_another_database [section.paths.default] tmpdir = /tmp array[] = first value array[] = second value array[] = third value array[] = fourth value ## API ### decode(inistring) Decode the ini-style formatted `inistring` into a nested object. ### parse(inistring) Alias for `decode(inistring)` ### encode(object, [section]) Encode the object `object` into an ini-style formatted string. If the optional parameter `section` is given, then all top-level properties of the object are put into this section and the `section`-string is prepended to all sub-sections, see the usage example above. ### stringify(object, [section]) Alias for `encode(object, [section])` ### safe(val) Escapes the string `val` such that it is safe to be used as a key or value in an ini-file. Basically escapes quotes. For example ini.safe('"unsafe string"') would result in "\"unsafe string\"" ### unsafe(val) Unescapes the string `val` ini-1.1.0/ini.js000066400000000000000000000102051207764173200134140ustar00rootroot00000000000000 exports.parse = exports.decode = decode exports.stringify = exports.encode = encode exports.safe = safe exports.unsafe = unsafe var eol = process.platform === "win32" ? "\r\n" : "\n" function encode (obj, section) { var children = [] , out = "" Object.keys(obj).forEach(function (k, _, __) { var val = obj[k] if (val && Array.isArray(val)) { val.forEach(function(item) { out += safe(k + "[]") + " = " + safe(item) + "\n" }) } else if (val && typeof val === "object") { children.push(k) } else { out += safe(k) + " = " + safe(val) + eol } }) if (section && out.length) { out = "[" + safe(section) + "]" + eol + out } children.forEach(function (k, _, __) { var nk = dotSplit(k).join('\\.') var child = encode(obj[k], (section ? section + "." : "") + nk) if (out.length && child.length) { out += eol } out += child }) return out } function dotSplit (str) { return str.replace(/\1/g, '\2LITERAL\\1LITERAL\2') .replace(/\\\./g, '\1') .split(/\./).map(function (part) { return part.replace(/\1/g, '\\.') .replace(/\2LITERAL\\1LITERAL\2/g, '\1') }) } function decode (str) { var out = {} , p = out , section = null , state = "START" // section |key = value , re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i , lines = str.split(/[\r\n]+/g) , section = null 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]) p = out[section] = out[section] || {} return } var key = unsafe(match[2]) , 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 (!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) , p = out , l = parts.pop() , nl = l.replace(/\\\./g, '.') parts.forEach(function (part, _, __) { 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 safe (val) { return ( typeof val !== "string" || val.match(/[\r\n]/) || val.match(/^\[/) || (val.length > 1 && val.charAt(0) === "\"" && val.slice(-1) === "\"") || val !== val.trim() ) ? JSON.stringify(val) : val.replace(/;/g, '\\;') } function unsafe (val, doUnesc) { val = (val || "").trim() if (val.charAt(0) === "\"" && val.slice(-1) === "\"") { 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 (c === "\\" || c === ";") unesc += c else unesc += "\\" + c esc = false } else if (c === ";") { break } else if (c === "\\") { esc = true } else { unesc += c } } if (esc) unesc += "\\" return unesc } return val } ini-1.1.0/package.json000066400000000000000000000006461207764173200145750ustar00rootroot00000000000000{ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "name": "ini", "description": "An ini encoder/decoder for node", "version": "1.1.0", "repository": { "type": "git", "url": "git://github.com/isaacs/ini.git" }, "main": "ini.js", "scripts": { "test": "tap test/*.js" }, "engines": { "node": "*" }, "dependencies": {}, "devDependencies": { "tap": "~0.0.9" } } ini-1.1.0/test/000077500000000000000000000000001207764173200132605ustar00rootroot00000000000000ini-1.1.0/test/bar.js000066400000000000000000000007261207764173200143670ustar00rootroot00000000000000//test that parse(stringify(obj) deepEqu var ini = require('../') var test = require('tap').test var data = { 'number': {count: 10}, 'string': {drink: 'white russian'}, 'boolean': {isTrue: true}, 'nested boolean': {theDude: {abides: true, rugCount: 1}} } test('parse(stringify(x)) deepEqual x', function (t) { for (var k in data) { var s = ini.stringify(data[k]) console.log(s, data[k]) t.deepEqual(ini.parse(s), data[k]) } t.end() }) ini-1.1.0/test/fixtures/000077500000000000000000000000001207764173200151315ustar00rootroot00000000000000ini-1.1.0/test/fixtures/foo.ini000066400000000000000000000016631207764173200164230ustar00rootroot00000000000000o = p a with spaces = b c ; wrap in quotes to JSON-decode and preserve spaces " xa n p " = "\"\r\nyoyoyo\r\r\n" ; wrap in quotes to get a key with a bracket, not a section. "[disturbing]" = hey you never know ; Test arrays zr[] = deedee ar[] = one ar[] = three ; This should be included in the array ar = this is included ; Test resetting of a value (and not turn it into an array) br = cold br = warm ; a section [a] av = a val e = { o: p, a: { av: a val, b: { c: { e: "this [value]" } } } } j = "{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }" "[]" = a square? ; Nested array cr[] = four cr[] = eight ; nested child without middle parent ; should create otherwise-empty a.b [a.b.c] e = 1 j = 2 ; dots in the section name should be literally interpreted [x\.y\.z] x.y.z = xyz [x\.y\.z.a\.b\.c] a.b.c = abc ; this next one is not a comment! it's escaped! nocomment = this\; this is not a comment ini-1.1.0/test/foo.js000066400000000000000000000043411207764173200144030ustar00rootroot00000000000000var i = require("../") , tap = require("tap") , test = tap.test , fs = require("fs") , path = require("path") , fixture = path.resolve(__dirname, "./fixtures/foo.ini") , data = fs.readFileSync(fixture, "utf8") , d , expectE = 'o = p\n' + 'a with spaces = b c\n' + '" xa n p " = "\\"\\r\\nyoyoyo\\r\\r\\n"\n' + '"[disturbing]" = hey you never know\n' + 'zr[] = deedee\n' + 'ar[] = one\n' + 'ar[] = three\n' + 'ar[] = this is included\n' + 'br = warm\n' + '\n' + '[a]\n' + 'av = a val\n' + 'e = { o: p, a: ' + '{ av: a val, b: { c: { e: "this [value]" ' + '} } } }\nj = "\\"{ o: \\"p\\", a: { av:' + ' \\"a val\\", b: { c: { e: \\"this [value]' + '\\" } } } }\\""\n"[]" = a square?\n' + 'cr[] = four\ncr[] = eight\n\n' +'[a.b.c]\ne = 1\n' + 'j = 2\n\n[x\\.y\\.z]\nx.y.z = xyz\n\n' + '[x\\.y\\.z.a\\.b\\.c]\na.b.c = abc\n' + 'nocomment = this\\; this is not a comment\n' , expectD = { o: 'p', 'a with spaces': 'b c', " xa n p ":'"\r\nyoyoyo\r\r\n', '[disturbing]': 'hey you never know', 'zr': ['deedee'], 'ar': ['one', 'three', 'this is included'], 'br': 'warm', a: { av: 'a val', e: '{ o: p, a: { av: a val, b: { c: { e: "this [value]" } } } }', j: '"{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }"', "[]": "a square?", cr: ['four', 'eight'], b: { c: { e: '1', j: '2' } } }, 'x.y.z': { 'x.y.z': 'xyz', 'a.b.c': { 'a.b.c': 'abc', 'nocomment': 'this\; this is not a comment' } } } test("decode from file", function (t) { var d = i.decode(data) t.deepEqual(d, expectD) t.end() }) test("encode from data", function (t) { var e = i.encode(expectD) t.deepEqual(e, expectE) var obj = {log: { type:'file', level: {label:'debug', value:10} } } e = i.encode(obj) t.notEqual(e.slice(0, 1), '\n', 'Never a blank first line') t.notEqual(e.slice(-2), '\n\n', 'Never a blank final line') t.end() })