1
0
Fork 0
master
Ondřej Hruška vor 6 Jahren
Ursprung 80e17ad329
Commit 0390122728
Signiert von: MightyPork
GPG-Schlüssel-ID: 2C5FD5035250423D
  1. 6
      .gitignore
  2. 5
      Makefile
  3. 38
      README.md
  4. 143
      nini.c
  5. 69
      nini.h
  6. 22
      test.c

6
.gitignore vendored

@ -0,0 +1,6 @@
*.obj
*.so
*.out
*.a
CMakeLists.txt
cmake-build-*/

@ -0,0 +1,5 @@
a.out: test.c nini.c
cc -Og test.c nini.c
run: a.out
./a.out

@ -1,2 +1,36 @@
# nini
Nano INI parser
# NINI
This parser aims to be the smallest possible while supporting a sufficient subset of the INI format.
The parser is meant for use in embedded applications.
When compiled for a bare metal Cortex-M0, **the whole parser fits in 336 bytes** of Flash and 30 (+buffers) bytes of RAM.
## Features
- Basic INI format support
- Accepts both Unix and DOS newlines
- Minimal memory footprint
- The input file can be loaded in multiple pieces of any size.
- Buffer sizes (section, key, value) can be configured in the header file.
- Custom data `void *` for maintaining user context, passed to the callback
## Sypported syntax
Any whitespace, except inside a value, is discarded.
- **Sections** - `[section.name]`
- **Key-value pairs** - `key.foo-bar_baz123 = value lorem ipsum 123`
- Value can contain whitespace, leading and trailing whitespace is removed.
- Ends with either `\r` or `\n`
- **Comment** `# comment ...`
- Ends with either `\r` or `\n`
## Limitations
- Not re-entrant, uses a static state variable
- No checks for invalid syntax
- Whitespace inside keys and section names is removed
- Quoted strings and escape sequences are not supported, will be collected as plain text
- Value can't be followed by an inline comment
See the file `test.c` for an example of the most basic usage; see the header file for more details on the API.

143
nini.c

@ -0,0 +1,143 @@
///
/// nini - Nano INI parser
///
/// Written by MightyPork, 2018
/// MIT license
///
#include "nini.h"
enum nini_state {
NINI_IDLE,
NINI_SECTION,
NINI_KEY,
NINI_VALUE,
NINI_COMMENT,
};
static struct {
uint8_t section_i;
char section[INI_KEY_MAX];
uint8_t key_i;
char key[INI_KEY_MAX];
uint8_t value_i;
char value[INI_VALUE_MAX];
bool val_last_space;
IniParserCallback cb;
void *userdata;
enum nini_state state;
} nini;
void ini_parse_begin(IniParserCallback callback, void *userData)
{
ini_parse_reset();
nini.cb = callback;
nini.userdata = userData;
}
void ini_parse(const char *data, size_t len)
{
for (; len > 0; len--) {
char c = *data++;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
if (nini.state != NINI_VALUE && nini.state != NINI_COMMENT)
continue;
}
switch (nini.state) {
case NINI_IDLE:
if (c == '[') {
nini.state = NINI_SECTION;
nini.section_i = 0;
}
else if (c == '#') {
nini.state = NINI_COMMENT;
}
else {
nini.state = NINI_KEY;
nini.key_i = 0;
nini.value_i = 0;
nini.val_last_space = false;
nini.key[nini.key_i++] = c;
}
break;
case NINI_COMMENT:
if (c == '\n' || c == '\r') {
nini.state = NINI_IDLE;
}
break;
case NINI_SECTION:
if (c == ']') {
nini.section[nini.section_i] = 0;
nini.state = NINI_COMMENT; // discard to EOL
break;
}
else if (nini.section_i < INI_KEY_MAX - 1) {
nini.section[nini.section_i++] = c;
}
break;
case NINI_KEY:
if (c == '=') {
nini.key[nini.key_i] = 0;
nini.state = NINI_VALUE;
}
else if (nini.key_i < INI_KEY_MAX - 1) {
nini.key[nini.key_i++] = c;
}
break;
case NINI_VALUE:
switch (c) {
case ' ':
case '\t':
if (nini.value_i) nini.val_last_space = true;
break;
case '\r':
case '\n':
nini.value[nini.value_i] = 0;
nini.state = NINI_IDLE;
nini.cb(nini.section, nini.key, nini.value, nini.userdata);
break;
default:
if (nini.val_last_space && nini.value_i < INI_VALUE_MAX - 1) {
nini.value[nini.value_i++] = ' ';
}
if (nini.value_i < INI_VALUE_MAX - 1) {
nini.value[nini.value_i++] = c;
}
nini.val_last_space = false;
}
}
}
}
void *ini_parse_end(void)
{
if (nini.state == NINI_VALUE) {
nini.value[nini.value_i] = 0;
nini.state = NINI_IDLE;
nini.cb(nini.section, nini.key, nini.value, nini.userdata);
}
return nini.userdata;
}
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
{
ini_parse_begin(callback, userData);
ini_parse(text, len);
ini_parse_end();
}
void ini_parse_reset(void)
{
nini.state = NINI_IDLE;
}

@ -0,0 +1,69 @@
///
/// nini - Nano INI parser
///
/// Written by MightyPork, 2018
/// MIT license
///
#ifndef INIPARSE_H
#define INIPARSE_H
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
// buffer sizes
#define INI_KEY_MAX 20
#define INI_VALUE_MAX 30
/**
* INI parser callback, called for each found key-value pair.
*
* @param section - current section, empty string for global keys
* @param key - found key (trimmed of whitespace)
* @param value - value, trimmed of quotes or whitespace
* @param userData - opaque user data pointer, general purpose
*/
typedef void (*IniParserCallback)(const char *section, const char *key, const char *value, void *userData);
/**
* Begin parsing a stream
*
* @param callback - key callback to assign
* @param userData - optional user data that will be passed to the callback
*/
void ini_parse_begin(IniParserCallback callback, void *userData);
/**
* End parse stream.
* Flushes what remains in the buffer and removes callback.
*
* @returns userData or NULL if none
*/
void* ini_parse_end(void);
/**
* Parse a string (needn't be complete line or file)
*
* @param data - string to parse
* @param len - string length (0 = use strlen)
*/
void ini_parse(const char *data, size_t len);
/**
* Parse a complete file loaded to string
*
* @param text - entire file as string
* @param len - file length (0 = use strlen)
* @param callback - key callback
* @param userData - optional user data for key callback
*/
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData);
/**
* Explicitly reset the parser
*/
void ini_parse_reset(void);
#endif // INIPARSE_H

@ -0,0 +1,22 @@
#include "nini.h"
#include <string.h>
const char *testfile =
"# Comment ...........\n"
"rootkey = Value\r\n"
"\r\n"
" [ Section@name!@+12346 ]\r\n"
"sec.key-a_b-1 = 444\r\n"
"sec.key2 = Test 123456 with spaces\r\n"
"[ this is a section with spaces nthat will be removed ]\n"
"this_one_has_no_eol = 123456";
void test_cb(const char *section, const char *key, const char *value, void *userData)
{
printf("[%s] >%s< = >%s<\r\n", section, key, value);
}
int main (void) {
ini_parse_file(testfile, strlen(testfile), test_cb, NULL);
}
Laden…
Abbrechen
Speichern