#ifndef __INIPARSE_H #define __INIPARSE_H #ifdef __cplusplus extern "C" { #endif /* Single function .INI file parser v1.00 (c) SysTools 2025 http://systools.losthost.org/ https://github.com/systoolz/miscsoft/blob/master/iniparse.h This code is distributed under Apache License 2.0 license: https://www.apache.org/licenses/LICENSE-2.0 ANSI C'89 native code without any dependencies (libraries, header files, constants or defines) - native "unsigned int" since "size_t" not available on all platforms - no other functions like memset() used - no standard constants or defines like NULL used - no defines for code shortening to avoid possible naming conflicts - no dependency to other libraries or code - neutral parser for different line endings (CR, LF or CRLF) - returned result buffer string always zero-terminated - NULL-section support, code can parse strings like " key1=value1 \n key2=value2 " - comment lines started with ";" supported - escaped values with double quote supported - ANSI C'89 compatible arguments: sect - section name, set to NULL if key without section or before first section declaration required key - key name to obtain def - default value to use if key not found, set to NULL to use empty string as default value res - result buffer rsz - result buffer size in characters including terminating zero character buf - .INI file data buffer bsz - .INI file data buffer size in characters */ static void ini_mem_get(char *sect, char *key, char *def, char *res, unsigned int rsz, unsigned char *buf, unsigned int bsz) { unsigned char map[256], low[256], *a, *b; unsigned int i, k; /* at least default value as result can be returned */ if ((!res) || (!rsz)) { return; } /* result always zero-terminated */ rsz--; /* no space for result, only for terminating zero */ if (!rsz) { *res = 0; return; } /* use default value if specified as result or empty string */ i = 0; if (def) { /* set default result */ while ((i < rsz) && (def[i])) { res[i] = def[i]; i++; } } res[i] = 0; /* sanity checks */ if ((!key) || (!buf) || (!bsz)) { return; } /* prepare fast check lists */ for (i = 0; i < 256; i++) { /* tolower() */ low[i] = ((i >= 'A') && (i <= 'Z')) ? (i + ('a' - 'A')) : i; /* do not use external functions like memset() */ map[i] = 0; } map['\t'] = 1; /* tab */ map['\n'] = 2; /* line feed */ map['\r'] = 2; /* carriage return */ map[' '] = 1; /* space */ map['"'] = 4; /* text quote */ map[';'] = 8; /* comment line */ map['='] = 16; /* value separator */ map['['] = 32; /* section start */ map[']'] = 64; /* section end */ i = 0; while (i < bsz) { /* line skip */ if (i) { while ((i < bsz) && (map[buf[i]] != 2)) { i++; } } /* skip all whitespaces or empty lines */ while ((i < bsz) && (map[buf[i]] & (1 | 2))) { i++; } if (i >= bsz) { break; } /* skip comment lines */ if (map[buf[i]] == 8) { i++; /* trigger line skip if file started with comment line */ continue; } /* section start */ if (map[buf[i]] == 32) { /* section already found or key without section required */ if (!sect) { break; } i++; /* skip whitespaces */ while ((i < bsz) && (map[buf[i]] == 1)) { i++; } if (i >= bsz) { break; } /* name start */ a = &buf[i]; b = a; k = 0; /* while not end of the line */ while ((i < bsz) && (map[buf[i]] != 2)) { /* little trick - save last non-empty character */ if (map[buf[i]] != 1) { /* section end reached - skip everything till the end of the line */ if (map[buf[i]] == 64) { break; } b = &buf[i]; } i++; } /* special case - empty section name */ if ((a == b) && (!*sect)) { /* section found */ sect = (char *) 0L; /* NULL */ } else { /* compare section name */ k = 0; while ((a <= b) && (sect[k])) { if (low[(unsigned char) sect[k]] != low[*a]) { break; } k++; a++; } /* section found */ if ((a > b) && (!sect[k])) { sect = (char *) 0L; /* NULL */ } } /* skip this line with section name */ continue; } /* section still not found - skip this line */ if (sect) { i++; /* trigger line skip if file not started with section name */ continue; } /* section found - find key */ k = 0; a = &buf[i]; b = a; while ((i < bsz) && (map[buf[i]] != 2)) { /* little trick - save last non-empty character */ if (map[buf[i]] != 1) { /* key-value separator found */ if (map[buf[i]] == 16) { i++; k = 1; break; } b = &buf[i]; } i++; } /* key-value separator not found - skip this line */ if (!k) { continue; } /* compare key name */ k = 0; while ((a <= b) && (key[k])) { if (low[(unsigned char) key[k]] != low[*a]) { break; } k++; a++; } /* key not found - skip this line */ if ((a <= b) || (key[k])) { continue; } /* skip whitespaces */ while ((i < bsz) && (map[buf[i]] == 1)) { i++; } if (i >= bsz) { break; } /* key found - get value */ k = 0; a = &buf[i]; b = a; while ((i < bsz) && (map[buf[i]] != 2)) { /* little trick - save last non-empty character */ if (map[buf[i]] != 1) { b = &buf[i]; k = 1; } i++; } /* no value */ if (!k) { break; } /* double-quoted value */ if (map[*a] == 4) { a++; /* only if value starts with double quote strip existing one from the tail */ if (map[*b] == 4) { b--; } } /* fill in result */ while ((a <= b) && (rsz)) { *res = *a; res++; a++; rsz--; } /* result always zero-terminated */ *res = 0; /* done */ break; } } #ifdef __cplusplus } #endif #endif
2025.06.14