SysTools Logo SysTools


C, ANSI C: Single function .INI file parser


#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


[ Код ]