gpii-universal
Version:
Cross platform, core components of the GPII personalization infrastructure.
176 lines (142 loc) • 4.71 kB
Markdown
# The INI file reader/writer.
Reads and writes INI files.
The main goal of this library is to only make small specific changes to an existing INI file, while preserving the
formatting of the rest of the file.
In order to implement this, the same thing could not be done with the original ini library,
[GPII/ini](https://github.com/GPII/ini), where the required changes were too significant for its original charm to
remain. So, while looking roughly the same from the outside, this is a re-write rather than a fork.
## INI file format
INI file can look something like this:
```ini
# comments
; other comments
# = or : delimiters.
key=value1
key2:value2
key3 = value3
# Sections (and sub-sections)
[section]
key="section-value"
[[subsection]]
key=subsection value
[section2]
# Multiple lines
multi-line="""line1
line2
line3"""
multi-line2=line1
line2
line3
```
Which will parse to the following:
```json5
{
key: "value1",
key2: "value2",
key3: "value3",
section: {
key: "section-value",
subsection: {key: "subsection value"}
},
section2: {
"multi-line": "line1\nline2\nline3",
"multi-line2": "line1\nline2\nline3"
}
}
```
See [test/read-test.ini](test/read-test.ini) for an extreme example.
When writing to an existing file, the content is modified rather than re-writing. Effort is made to respect the
unchanged values and current format of the file. An updated version of the above example will look like this:
```ini
# comments
; other comments
# = or : delimiters.
key=new value
key2:another new value
key3 = and again
# Sections
[section]
key="quotes respected"
# sub-sections
[[subsection]]
key=subsection value
new1=new value
[section2]
# Multiple lines
multi-line="""modified line1
modified line2
modified line3"""
multi-line2=modified line1
modified line2
modified line3
[newSection]
newValue=value
```
Compare [test/write-test.ini](test/write-test.ini) with [test/write-test.expect.ini](test/write-test.expect.ini) for an
example.
## Usage
### Reading
```snippet
/**
* Reads INI file content, returning the parsed data as an object.
*
* @param {String} content The ini file content.
* @param {Object} options Options:
* @param {Boolean} options.strings true to always return strings, otherwise try to return numbers and booleans for
* unquoted values, where appropriate.
* @param {Boolean} options.firstDuplicate true to use the first occurrence of a value with a duplicate name, otherwise
* the last is used. [default: false]
*
* @return {Object} The parsed ini file data.
*/
gpii.iniFile.read = function (content, options) {
// ...
};
```
### Writing
```snippet
/**
* Writes an object to existing INI file content.
*
* @param {String} input The ini file content to update (can be empty).
* @param {Object} data The new settings data.
* @param {iniFile.WriteOptions} options INI file output options.
* @return {String} The new ini file content.
*/
gpii.iniFile.write = function (input, data, options) {
// ...
};
```
```javascript
/**
* Options for writing ini files with gpii.iniFile.write().
*
* @typedef {Object} iniFile.WriteOptions
*
* @property {String} keyValueDelimiter - Text which separates keys and values, for new values. [default: "="].
* @property {Boolean} keepUndefined - true to keep items and sections that are not in data. Use to define values
* without reading the file beforehand.
* @property {String} multilineStyle - How new multi-line values are written:
* "'''" or '"""': Surround the value with 3x single or double quotes (default).
* "indent": Indent the additional lines.
* "escape": Use an escaped n (\n).
* anything else: Wrap the value with the given value.
* @property {String} quote - For new values, quote them "always", "never", "strings" (for only strings), or
* "spaces" (if the value starts or ends with a space) [default: "spaces"].
* @property {String} quoteChar - The quote character for new values when quoting. [default: " (double quote)].
* @property {String} eol - The file's end of line character(s). [default: auto-detect]
*/
```
## How it works
Parsing is performed by a single regular expression ([ini.regex](ini.regex)), which calls a function when it matches
either a section header (`[example]`), or a `key=value` pair.
When reading data, it fills an object with the matched values.
When writing, it will replace the matches with text from an existing object. Only the text in the value is replaced, and
only if it is different to the stored value.
## Limitations
Sub-sections can't have the same name as a value in the same section, eg:
```ini
[section1]
same-name=value
[[same-name]]
```