Skip to content

Instantly share code, notes, and snippets.

@nicholatian
Last active April 11, 2025 12:10
Show Gist options
  • Select an option

  • Save nicholatian/05cae747b0d3a8928c85c12d65187ff3 to your computer and use it in GitHub Desktop.

Select an option

Save nicholatian/05cae747b0d3a8928c85c12d65187ff3 to your computer and use it in GitHub Desktop.

INI schema

An INI schema for validating INI schemas

Written by Alexander Nicholi https://nich.fi/
Copyright © 2025 Aquefir Consulting LLC https://aquefir.co/
Released under the Creative Commons Attribution-NoDerivatives 3.0 Unported license

Licence

You are free to:

  • Share — copy and redistribute the material in any medium or format for any purpose, even commercially.

The licensor cannot revoke these freedoms as long as you follow the license terms.

Under the following terms:

  • Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
  • NoDerivatives — If you remix, transform, or build upon the material, you may not distribute the modified material.

No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

Read the licence deed and the full legal code on the Web at https://creativecommons.org/licenses/by-nd/3.0/.


So you want to understand INI schemas.

Overview

First, some basic rules:

  1. All keys and values are not whitespace-stripped in any direction.
    • This makes INIs behave like Hex Workshop TBL files; for example, a line  bla===  is a key named bla with a value of == .
  2. Coterminus with rule 1, equal signs are not possible in keys.
  3. Likewise, square brackets are also not possible in section names.
  4. All regular expression parsing is done to the substrings in question; this implies newline characters cannot appear in test strings for said regular expressions.
  5. INIs do not have ‘data types’, ergo there are only strings here.
  6. No concept of ‘nesting’ is provided with sections or keys. There is ONE level of hierarchy: sections and their entries.
  7. This is a schema for parsing INI files, not lexing them; concerns regarding text encoding or newline styles are out of scope here.
  8. INI schema parsers should at a minimum support the PCRE subset provided by the Super Light Regular Expressions Library.

With all of that said, INI schemas take a simple approach that makes real world usage of them fairly self-evident, even though it makes this schema (the ‘meta-schema’) read a bit like performance art.

The format

INI schemas use section names with some special formatting rules to contain their valid form, key names, and characteristics. This is elucidated mechanistically in this meta-schema, but in English:

  1. All schemas must have two global key-value entries named caseinsens and mscomments which decides whether section names and key names are treated case-insensitively and whether semicolons are used instead of hashes to start line comments, respectively.
    • This is provided to make Win32 API compatibility tractable.
  2. Every section without a colon describes a section formula.
    • Section formulae have one key-value entry called val where a regular expression constrains the possible values inside the square brackets.
  3. Every section with a colon describes an entry formula, where the portion before the colon refers to an existing section formula, and the portion after the colon denotes the literal key name.
    • Entry formulae have one key-value entry called val where a regular expression constrains the possible values on the other side of the equal sign from the key name.
  4. Entry formulae may also be suffixed with a plus sign +; this means they may appear under the same name multiple times in a given INI file, but are required to appear at least once.
  5. Entry formulae may also be suffixed with an asterisk *; this means they may appear under the same name multiple times in a given INI file, and are also optional and so may be omitted.
  6. Entry formulae may also be suffixed with a question mark ?; this means they are optional and so may be omitted.
  7. Sections themselves can be marked as optional with a question mark ?, marked to repeat with a plus sign +, or marked as optional and repeating with an asterisk * in the same way that entry formulae are.
  8. Every section with a colon where the left hand side is empty, ergo they begin with the string [:, are entry formulae for the global section.
    • The ‘global section’ is simply the list of key-value pairs that comes before any sections appear in the INI file.
  9. Only line comments are allowed.
    1. If hashes or semicolons appear after a value has started, they are considered part of the value.
    2. If hashes or semicolons appear on a section line or before an equal sign after a value has started, it is an error.
    3. Line comments MAY be preceded by whitespace, ergo a line of the form # comment is valid.

Understanding section identifiers

It’s best to understand the left hand side of an entry formula as a kind of identifier that one uses by reference in their schema design. The right hand side constitutes a literal and is used as-is to find and validate keys, but in the case of section formulae where there is no right hand side, the contents are still an identifier and don't show up in the files themselves.

A note on file extensions

While INIs themselves are typically found with the .ini extension, schemas should use the .inf extension instead, since generally they do constitute ‘setup information’ and besides being treated synonymously by virtually all tools give a nice sense of contrast to denote the fact that they are schemas. Therefore, this file has a canonical name of schema.inf. 😊

The meta-schema itself

caseinsens=0
mscomments=0

[:caseinsens]
val=^[01]$

[globalsection]
val=^:[^:\+\*\?]+[\+\*\?]?$

[globalsection:val]
val=^.+$

[section]
val=^[^:\+\*\?]+[\+\*\?]?$

[section:val]
val=^.+$

[reqentry]
val=^[^:\+\*\?]+:[^:\+\*\?]+$

[reqentry:val]
val=^.+$

[optentry]
val=^[^:\+\*\?]+:[^:\+\*\?]+\?$

[optentry:val]
val=^.+$

[multientry]
val=^[^:\+\*\?]+:[^:\+\*\?]+\*$

[multientry:val]
val=^.+$

GitHub Gist ID: 05cae747b0d3a8928c85c12d65187ff3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment