parseIni function

Map<String, Map<String, String>> parseIni(
  1. String input, {
  2. bool allowExport = false,
})

Parses input as INI text into section → key → value. Entries before the first [section] land under iniGlobalSection (''). A declared but empty [section] still appears as an empty map. The separator is the first = on the line (so url=http://x keeps the colon in the value). Blank lines and full-line # / ; comments are skipped; any other line lacking = throws a FormatException naming the offending line — strict by design so a typo in config surfaces instead of being silently dropped.

Set allowExport to strip a leading export from keys (shell-style files).

Example:

parseIni('[db]\nhost = localhost\nport = 5432');
// {db: {host: localhost, port: 5432}}

Audited: 2026-06-12 11:26 EDT

Implementation

Map<String, Map<String, String>> parseIni(String input, {bool allowExport = false}) {
  final Map<String, Map<String, String>> out = <String, Map<String, String>>{};
  String section = iniGlobalSection;
  for (final String raw in input.split('\n')) {
    final String line = raw.trim();
    if (line.isEmpty || _isComment(line)) {
      continue;
    }
    // A `[name]` header opens (or re-opens) a section; create it eagerly so an
    // empty section is still observable, then move on to the next line.
    final String? header = _sectionName(line);
    if (header != null) {
      section = header;
      out.putIfAbsent(section, () => <String, String>{});
      continue;
    }
    final MapEntry<String, String> entry = _entry(raw, line, allowExport: allowExport);
    out.putIfAbsent(section, () => <String, String>{})[entry.key] = entry.value;
  }
  return out;
}