Skip to content

tigerkelly/ining

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

IniNG — Ini Next Gen

A lightweight C library for reading, writing, and modifying IniNG-format .ini files entirely in memory. All mutations are implemented as string search-and-replace operations on a single heap buffer, so comments, indentation, and whitespace are preserved on every round-trip.


File Format

IniNG extends the classic .ini format with an explicit two-level hierarchy: groups contain sites, and sites contain key/value pairs. Comments (;) are supported on any line.

# IniNg - Ini Next Gen

{GroupOne}                      ; Group One
    [kelly]                     ; Site kelly
        address = 192.168.128.23
        port    = 8554
        type    = RTSP TCP
        user    = root
        password = agilemesh1
        isCell  = false
    [End]
    [Wiles]                     ; Site Wiles
        address = 192.168.128.24
        port    = 554
        type    = RTSP UDP
        user    = root
        password = agilemesh1
        isCell  = false
    [End]
{End}

{GroupTwo}                      ; Group Two
    [MegaPixel]
        address = 192.168.129.33
        port    = 8554
        type    = RTSP TCP
        user    = root
        password = agilemesh1
        isCell  = false
    [End]
{End}

Rules:

  • Groups are delimited by {GroupName}{End}
  • Sites are delimited by [SiteName][End]
  • Key/value pairs use key = value (whitespace around = is ignored)
  • Inline comments start with ;
  • All names are case-insensitive
  • Leading whitespace (tabs/spaces) before [ and { is allowed and preserved

Building

make          # builds libining.a
make test     # builds and runs the test suite (45 tests)
make clean    # removes build artifacts

Requires a C11-compatible compiler (gcc or clang). No external dependencies.

To link against your own project:

gcc -std=c11 myapp.c ining.c -o myapp
# or with the static library:
gcc -std=c11 myapp.c -L. -lining -o myapp

API Reference

All functions are declared in ining.h. The opaque handle IniNG * owns the in-memory buffer. Call iningSave() to persist changes and iningFree() when done.

Lifecycle

// Load a file from disk into memory. Returns NULL on error.
IniNG *iningLoad(const char *filename);

// Parse an in-memory string instead of a file.
IniNG *iningParse(const char *text);

// Write the (possibly modified) document back to disk.
// Pass NULL to write back to the original file.
// Returns 0 on success, -1 on error.
int iningSave(IniNG *ini, const char *filename);

// Return the current document as a heap-allocated string. Caller must free().
char *iningToString(const IniNG *ini);

// Free all memory.
void iningFree(IniNG *ini);

Read

// Get a value. Returns NULL if group, site, or key is not found.
// The returned pointer is valid until the next mutating call.
const char *iningGet(const IniNG *ini,
                     const char *group,
                     const char *site,
                     const char *key);

// Return non-zero if the group exists.
int iningHasGroup(const IniNG *ini, const char *group);

// Return non-zero if the site exists within the group.
int iningHasSite(const IniNG *ini, const char *group, const char *site);

Write

All write operations modify the in-memory buffer only. Call iningSave() to persist.

// Set a key to a new value, or add the key if it does not exist.
// Returns 0 on success, -1 if the group or site is not found.
int iningSet(IniNG *ini,
             const char *group,
             const char *site,
             const char *key,
             const char *value);

// Add a new group. Returns 0 on success, -1 if it already exists.
int iningAddGroup(IniNG *ini, const char *group, const char *comment);

// Add a new site inside an existing group.
// Returns 0 on success, -1 if the group is not found or site already exists.
int iningAddSite(IniNG *ini,
                 const char *group,
                 const char *site,
                 const char *comment);

Delete

// Delete a single key from a site. Returns 0 on success, -1 if not found.
int iningDeleteKey(IniNG *ini,
                   const char *group,
                   const char *site,
                   const char *key);

// Delete an entire site and all its keys. Returns 0 on success, -1 if not found.
int iningDeleteSite(IniNG *ini, const char *group, const char *site);

// Delete an entire group and all its sites/keys. Returns 0 on success, -1 if not found.
int iningDeleteGroup(IniNG *ini, const char *group);

Iteration

// Fill *names with a NULL-terminated array of group name strings.
// *count receives the number of groups found.
// Caller must free(*names) but NOT the individual strings.
int iningListGroups(const IniNG *ini, const char ***names, int *count);

// Same, for sites within a group.
int iningListSites(const IniNG *ini, const char *group,
                   const char ***names, int *count);

Usage Examples

Load, read, modify, and save

#include "ining.h"
#include <stdio.h>

int main(void) {
    IniNG *ini = iningLoad("cameras.ini");
    if (!ini) { perror("load failed"); return 1; }

    // Read a value
    const char *addr = iningGet(ini, "GroupOne", "kelly", "address");
    printf("kelly address: %s\n", addr);   // 192.168.128.23

    // Update an existing key
    iningSet(ini, "GroupOne", "kelly", "port", "9000");

    // Add a new key to an existing site
    iningSet(ini, "GroupOne", "kelly", "timeout", "30");

    // Add a brand-new group and site
    iningAddGroup(ini, "GroupThree", "Offsite cameras");
    iningAddSite (ini, "GroupThree", "Roof", "Rooftop camera");
    iningSet(ini, "GroupThree", "Roof", "address",  "10.0.0.50");
    iningSet(ini, "GroupThree", "Roof", "port",     "554");
    iningSet(ini, "GroupThree", "Roof", "type",     "RTSP TCP");

    // Delete a site and a key
    iningDeleteSite(ini, "GroupTwo", "MegaPixel");
    iningDeleteKey (ini, "GroupOne", "Wiles", "password");

    // Save (overwrites the original file)
    iningSave(ini, NULL);
    iningFree(ini);
    return 0;
}

Iterate over groups and sites

const char **groups;
int gcount;
iningListGroups(ini, &groups, &gcount);

for (int i = 0; i < gcount; i++) {
    printf("Group: %s\n", groups[i]);

    const char **sites;
    int scount;
    iningListSites(ini, groups[i], &sites, &scount);

    for (int j = 0; j < scount; j++) {
        const char *addr = iningGet(ini, groups[i], sites[j], "address");
        printf("  [%s] address=%s\n", sites[j], addr ? addr : "(none)");
        free((char *)sites[j]);
    }
    free(sites);
    free((char *)groups[i]);
}
free(groups);

Parse from a string

const char *text =
    "{Cameras}\n"
    "    [Lobby]\n"
    "        address = 192.168.1.10\n"
    "        port = 554\n"
    "    [End]\n"
    "{End}\n";

IniNG *ini = iningParse(text);
printf("%s\n", iningGet(ini, "Cameras", "Lobby", "address"));
iningFree(ini);

Design Notes

  • Single buffer, no tree. The document is stored as one char * string. Mutations splice new text in and out via buf_replace(). This keeps the implementation simple and guarantees the output is always human-readable.
  • Zero external dependencies. Only the C standard library and POSIX strncasecmp / strdup.
  • Case-insensitive name matching. Group, site, and key lookups all use strncasecmp, so GroupOne, groupone, and GROUPONE all refer to the same group.
  • Comments and whitespace preserved. Because edits are positional string splices rather than full serialisations, untouched lines come out exactly as they went in.
  • iningGet() return value lifetime. The returned pointer is into a small internal scratch buffer that is overwritten on the next call to iningGet(). Copy the value with strdup() if you need to keep it across calls.

Files

File Description
ining.h Public API header
ining.c Library implementation
test_ining.c Test suite (45 tests)
Makefile Build rules

About

The next generation ini file library.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors