Skip to content

Instantly share code, notes, and snippets.

@ssleert
Last active September 28, 2023 22:38
Show Gist options
  • Save ssleert/c80c42dae753ad492778f0d818c54f41 to your computer and use it in GitHub Desktop.
Save ssleert/c80c42dae753ad492778f0d818c54f41 to your computer and use it in GitHub Desktop.
Super simple stack-only ini parser for embedded systems in C
#include "ini.h"
#include <assert.h>
#include <string.h>
// return pointer to truncated string
static inline char* StrTruncate(char s[]) {
if (s[0] == '\0') {
return s;
}
char* Str = s;
for (;
*Str == ' ';
++Str
);
char* StrEnd = Str + strlen(Str) - 1;
for (;
*StrEnd == ' ' && StrEnd > Str;
--StrEnd
);
StrEnd[1] = '\0';
return Str;
}
int32_t IniLoad(FILE* Stream, IniCallBack* Func) {
assert(Stream != NULL && "file stream is NULL");
assert(Func != NULL && "callback func is NULL");
char Section[__INI_H_BUFSIZE + 1]; size_t SectionIdx = 0;
char Key[__INI_H_BUFSIZE + 1]; size_t KeyIdx = 0;
char Value[__INI_H_BUFSIZE + 1]; size_t ValueIdx = 0;
INI_LEXEME Lexeme = INI_LEXEME_Key;
INI_LEXEME PrevLexeme = Lexeme;
size_t CallBackCount = 0;
while (!feof(Stream)) {
char ch = fgetc(Stream);
switch (ch) {
case '#': case ';':
PrevLexeme = Lexeme;
Lexeme = INI_LEXEME_Comment;
continue;
case '[':
Lexeme = INI_LEXEME_Section;
SectionIdx = 0;
*Section = '\0';
continue;
case ']':
Lexeme = INI_LEXEME_Key;
Section[SectionIdx] = '\0';
continue;
case '=':
if (Lexeme == INI_LEXEME_Value) {
goto ValueHandle;
}
Lexeme = INI_LEXEME_Value;
Key[KeyIdx] = '\0';
KeyIdx = 0;
continue;
case '\n':
Value[ValueIdx] = '\0';
if (Lexeme == INI_LEXEME_Value
|| (Lexeme == INI_LEXEME_Comment
&& PrevLexeme == INI_LEXEME_Value)
) {
size_t CallBackResult = Func(
StrTruncate(Section),
StrTruncate(Key),
StrTruncate(Value)
);
if (CallBackResult != 0) {
return CallBackResult;
}
CallBackCount++;
}
Lexeme = INI_LEXEME_Key;
KeyIdx = 0;
ValueIdx = 0;
continue;
default:
ValueHandle:
switch (Lexeme) {
case INI_LEXEME_Section:
if (SectionIdx == __INI_H_BUFSIZE) {
continue;
}
Section[SectionIdx] = ch;
SectionIdx++;
continue;
case INI_LEXEME_Key:
if (KeyIdx == __INI_H_BUFSIZE) {
continue;
}
Key[KeyIdx] = ch;
KeyIdx++;
continue;
case INI_LEXEME_Value:
if (ValueIdx == __INI_H_BUFSIZE) {
continue;
}
Value[ValueIdx] = ch;
ValueIdx++;
continue;
default: continue;
}
}
}
// ini file is incorrect
if (CallBackCount < 1) {
return 1;
}
return 0;
}
// Super simple stack-only ini parser
// for embedded systems in C
// 2023 Simon Ryabinkov <smnrbkv@proton.me>
#ifndef __INI_H__
#define __INI_H__
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define __INI_H_BUFSIZE 4096
// func that was called on all values in .ini file
typedef int32_t (IniCallBack)(
const char Section[],
const char Key[],
const char Val[]
);
typedef enum {
INI_LEXEME_Section = 1,
INI_LEXEME_Key,
INI_LEXEME_Value,
INI_LEXEME_Comment
} INI_LEXEME;
#define INI_LEXEME_LEN 4
static inline bool IniCheckValue(
const char Section[], const char Key[],
const char NeededSection[], const char NeededKey[]
) {
return (
strcmp(Section, NeededSection) == 0 &&
strcmp(Key, NeededKey) == 0
);
}
// Stream - opened file stream
// Func - return non-zero to stop, error is passed back through function
// return - 0 on success
int32_t IniLoad(FILE* Stream, IniCallBack* Func);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment