Skip to content

API-Beast/ini.h

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

ini.h

Inspired by sj.h which implements JSON in 150 lines of code.

  • A single header file.
  • Just 16 LOC for parsing standard INI files.
  • +32 LOC for optional nested array and dictionary support.
  • No memory allocations.
  • Note that parsing modifies the underlying buffer to insert '\0' bytes. Make sure the parsed string is writeable.
  • Very basic error checking: returns 0 if invalid INI was encountered, returns 1 if it was valid.

Basic Usage

To parse this file.

; This is a comment
[window]
width = 800
height = 600

We only need to call the parse_ini function in a loop.

parse_ini takes a pointer to the file buffer, as well as various string pointers to which it will write the parsed sectors, keys and values.

char *cursor = file_contents, *sector = NULL, *key = NULL, *value = NULL;
while(*cursor != '\0')
{
  if(!parse_ini(&cursor, &sector, &key, &value)){ printf("INI syntax error when parsing sector \"%s\", key \"%s\", value \"%s\".", sector, key, value); }
  if(key == NULL && value == NULL){ /* New sector */}
  // Basic values
  if(strcmp(sector, "window") == 0)
  {
     if(strcmp(key, "width") == 0) config.window_width = atoi(value);
     if(strcmp(key, "height") == 0) config.window_height = atoi(value);
     /* etc. */
  }
}

Extended INI

Normal INI files do not support arrays and dictionaries. We can optionally support them by calling the parse_ini_array and parse_ini_dict functions.

These functions need some space to write the keys and values to. The last element is how many elements the passed-in arrays can hold.

[app]
file_history = ["/user/files/abc.png", "/user/files/foo.png", "/user/files/bar.png"]
keymap = {"ctrl+a" = "select_all", "tab" = "open_menu"}
char *cursor = file_contents, *sector = NULL, *key = NULL, *value = NULL;
while(*cursor != '\0')
{
  if(!parse_ini(&cursor, &sector, &key, &value)){ printf("INI syntax error when parsing sector \"%s\", key \"%s\", value \"%s\".", sector, key, value); }
  if(strcmp(sector, "app") == 0)
  {
     if(strcmp(key, "file_history") == 0)
     {
        char* items[16]; int length = 0;
        if(!parse_ini_array(value, items, &length, 16)){ printf("INI syntax error when parsing array items in \"%s\"/\"%s\" value \"%s\".", sector, key, value); }
        for(int i = 0; i < length; i++)
            app.file_history[i] = items[i];
        app.num_file_history_items = length;
     }
     if(strcmp(key, "keymap") == 0)
     {
        char* keys[128]; char* values[128]; int length = 0;
        if(!parse_ini_dict(value, keys, values, &length, 128)){ printf("INI syntax error when parsing dictionary items in \"%s\"/\"%s\" value \"%s\".", sector, key, value); }
        for(int i = 0; i < length; i++)
            dict_insert_value(app.keymap, keys[i], values[i]);
     }
     /* etc. */
  }
}

Nested Arrays / Dictionaries

The parse_ini_array and parse_ini_dict functions also handle nesting.

Just call them again on the value returned if that value starts with '[' or '{' respectively. If you don't know the exact depth beforehand, for for example serialization or deserialization you can use recursion.

[network]
blacklist = [{ip=192.168.132.8, expires=01/10/2026, issuer=Matthew, comment="Go away Darren."}, {ip=192.168.132.7, expires=01/01/2099, issuer=Darren, comment="Until Hell freezes over."}]
if(strcmp(sector, "network") == 0)
{
   if(strcmp(key, "blacklist") == 0)
   {
      char* items[256]; int length = 0;
      if(!parse_ini_array(value, items, &length, 16)){ printf("INI syntax error when parsing array items in \"%s\"/\"%s\" value \"%s\".", sector, key, value); }
      for(int i = 0; i < length; i++)
      {
         // Each item is a dictionary
         char* subkeys[8]; char* subvalues[8]; int sublen = 0;
         if(!parse_ini_dict(items[i], subkeys, subvalues, &sublen, 8)){ printf("INI syntax error when parsing dictionary items in \"%s\"/\"%s\" array item \"%s\".", sector, key, items[i]); }
         char* ip = NULL, *expires = NULL, *issuer = NULL, *comment = NULL;
         for(int j = 0; j < sublen; j++)
         {
            if(strcmp(subkeys[j], "ip") == 0) ip = subvalues[j];
            if(strcmp(subkeys[j], "expires") == 0) expires = subvalues[j];
            if(strcmp(subkeys[j], "issuer") == 0) issuer = subvalues[j];
            if(strcmp(subkeys[j], "comment") == 0) comment = subvalues[j];
         }
         network.blacklist[i] = {ip, expires, issuer, comment};
      }
      network.num_blacklist_items = length;
   }
   /* etc. */
}

About

Minimal ini parser in 16 LOC. +Nested arrays/dictionaries in 48 LOC.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages