ini

An ini configuration file parser
git clone https://noulin.net/git/ini.git
Log | Files | Refs | LICENSE

commit 0a001d30181bf6eacfba9d43a3e9995ae0bf6eb8
parent 0de89e7f815217e06c413552a445c3ddeb8d6526
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sat, 19 May 2018 23:58:14 +0200

ini parser package

example/ini.conf |  16 +++++
example/main.c   |  37 ++++++++++
package.yml      |  18 +++++
src/.gdb_history |  14 ++++
src/ini.c        | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/ini.h        |  22 ++++++
6 files changed, 311 insertions(+)

Diffstat:
Aexample/ini.conf | 16++++++++++++++++
Aexample/main.c | 37+++++++++++++++++++++++++++++++++++++
Apackage.yml | 18++++++++++++++++++
Asrc/.gdb_history | 14++++++++++++++
Asrc/ini.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ini.h | 22++++++++++++++++++++++
6 files changed, 311 insertions(+), 0 deletions(-)

diff --git a/example/ini.conf b/example/ini.conf @@ -0,0 +1,16 @@ +project = ini +version = 0.1.0 + test = no +#ignore +[config] +name = "Michael Lazear" +date = 12/9/1873 +#[data] +town = Margaritaville +age = NaN +[output] +format=utf-8 +language=english +long="This string +spans multiple +lines" diff --git a/example/main.c b/example/main.c @@ -0,0 +1,37 @@ +#include <stdio.h> +#include <stdlib.h> +#include "../src/ini.h" +#include "libsheepyObject.h" + +int callback(char* section, char* config, char* value) +{ + printf("[%s] \"%s\" == \"%s\"\n", section, config, value); + return 0; +} + +int main(int argc, char** argv) { + if (argc < 2) { + printf("usage: %s file ...\noutput ini configuration values\n", argv[0]); + return EXIT_FAILURE; + } + + smallDictt *ini = parseIni(argv[1]); + //logVarG(ini); + createAllocateSmallJson(js); + setTopG(js, (baset*) ini); + logNFree(stringifyG(js,2)); + XSUCCESS + + int i; + for (i = 1; i < argc; i++) { + printf("parsing %s\n", argv[i]); + FILE* fp = fopen(argv[i], "r"); + if (! fp) { + fprintf(stderr, "Error opening %s\n", argv[i]); + return EXIT_FAILURE; + } + ini_parse_file(fp, callback); + fclose(fp); + } + return EXIT_SUCCESS; +} diff --git a/package.yml b/package.yml @@ -0,0 +1,18 @@ +--- + name: ini + version: 0.0.4 + description: An ini configuration file parser + bin: ./src/ini.c + scripts: + test: echo "Error no test specified" && exit 1 + repository: + type: git + url: git+https://github.com/RemyNoulin/ini.git + keywords: + - utility + - command + author: Remy Noulin + license: MIT + bugs: + url: https://github.com/RemyNoulin/ini/issues + homepage: https://github.com/RemyNoulin/ini diff --git a/src/.gdb_history b/src/.gdb_history @@ -0,0 +1,14 @@ +b main +r +n +p mainFilename +n +b 311 +c +n +n +record +b 311 +r +n +c diff --git a/src/ini.c b/src/ini.c @@ -0,0 +1,204 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include "ini.h" + +internal int read_until(FILE* fp, char* buffer, int size, int delim); + +internal int callback(char* section, char* config, char* value, smallDictt *r) +{ + //printf("[%s] \"%s\" == \"%s\"\n", section, config, value); + if (isBlankG(section)) { + setG(r, config, value); + } + else { + smallDictt *sect = getG(r, rtSmallDictt, section); + if (sect) { + setG(sect, config, value); + setPG(r, section, sect); + } + else { + sect = allocG(rtSmallDictt); + setG(sect, config, value); + setNFreeG(r, section, sect); + } + } + return 0; +} + +/** + * @brief Parse an ini file. + * @details Parse an ini file. Passes values to a callback function. + * No dynamic memory is allocated. + * + * @param fp Open file pointer to read INI file from + * @param callback Callback function accepting three arguments: + * section, variable, and value strings. + * @return 0 on success, error code on failure + */ +internal int iniParseFile(FILE* fp, int (*callback)(char*, char*, char*, smallDictt *), smallDictt *r) +{ + if ((fp == NULL) || (callback == NULL)) + return -1; + + char section [BUFFER_SIZE] = ""; + char config [BUFFER_SIZE] = ""; + char value [BUFFER_SIZE] = ""; + int c; + int err; + while((c = getc(fp)) != EOF) { + switch(c) { + case '#': { + while ((c = getc(fp)) != '\n') + ; + break; + } + case '[': { + err = read_until(fp, section, BUFFER_SIZE, ']'); + if (err) + return err; + break; + } + case '=': { + err = read_until(fp, value, BUFFER_SIZE, '\n'); + if (err) + return err; + err = callback(section, config, value, r); + if (err) + return err; + break; + } + case '\n': case ' ': case '\t': + break; + default: + ungetc(c, fp); + err = read_until(fp, config, BUFFER_SIZE, '='); + if (err) + return err; + break; + } + } + return -1; +} + +smallDictt *parseIni(const char *filename) +{ + FILE* fp = fopen(filename, "r"); + pTestErrorCmd(fp == NULL, return NULL); + + createAllocateSmallDict(r); + iniParseFile(fp, callback, r); + fclose(fp); + return r; +} + +internal int read_until(FILE* fp, char* buffer, int size, int delim) +{ + char c; + int i = 0; + memset(buffer, 0, size); + int string = 0; + while (i < size) { + c = getc(fp); + if(isblank(c) && string == 0) { + continue; + } else if (c == '\"') { + string = 1; + } else if (c == delim) { + buffer[i] = '\0'; + if (delim == '=') + ungetc(c, fp); + return 0; + } else if (c == EOF) { + return 0; + } + buffer[i++] = c; + } + buffer[i] = '\0'; + return -1; +} + +/** + * @brief Parse an ini file. + * @details Parse an ini file. Passes values to a callback function. + * No dynamic memory is allocated. + * + * @param fp Open file pointer to read INI file from + * @param callback Callback function accepting three arguments: + * section, variable, and value strings. + * @return 0 on success, error code on failure + */ +int ini_parse_file(FILE* fp, int (*callback)(char*, char*, char*)) +{ + if ((fp == NULL) || (callback == NULL)) + return -1; + + char section [BUFFER_SIZE] = ""; + char config [BUFFER_SIZE] = ""; + char value [BUFFER_SIZE] = ""; + int c; + int err; + while((c = getc(fp)) != EOF) { + switch(c) { + case '#': { + while ((c = getc(fp)) != '\n') + ; + break; + } + case '[': { + err = read_until(fp, section, BUFFER_SIZE, ']'); + if (err) + return err; + break; + } + case '=': { + err = read_until(fp, value, BUFFER_SIZE, '\n'); + if (err) + return err; + err = callback(section, config, value); + if (err) + return err; + break; + } + case '\n': case ' ': case '\t': + break; + default: + ungetc(c, fp); + err = read_until(fp, config, BUFFER_SIZE, '='); + if (err) + return err; + break; + } + } + return -1; +} + +bool saveIni(smallDictt *ini, const char *path) { + if (!ini || !path) { + return false; + } + createAllocateSmallArray(r); + forEachSmallDict(ini, k, D) { + cast(smallDictt*, d, D); + pushNFreeG(r, catS("[", k, "]")); + {forEachSmallDict(d, k2, S) { + castS(s, S); + if (isBlankG(s)) { + pushNFreeG(r, catS(k2, " =")); + } + else { + pushNFreeG(r, catS(k2, " = ", ssGet(s))); + } + finishG(s); + } + listFreeS(libsheepyInternalKeys); + } + pushG(r, ""); + finishG(d); + } + listFreeS(libsheepyInternalKeys); + writeFileG(r, path); + return true; +} + diff --git a/src/ini.h b/src/ini.h @@ -0,0 +1,22 @@ +#ifndef _INI_H_ +#define _INI_H_ + +#include "libsheepyObject.h" + +#define BUFFER_SIZE 16384 + +/** + * @brief Parse an ini file. + * @details Parse an ini file. Passes values to a callback function. + * No dynamic memory is allocated. + * + * @param fp Open file pointer to read INI file from + * @param callback Callback function accepting three arguments: + * section, variable, and value strings. + * @return 0 on success, error code on failure + */ +int ini_parse_file(FILE* fp, int (*callback)(char* section, char* variable, char* value)); + +smallDictt *parseIni(const char *filename); +bool saveIni(smallDictt *ini, const char *path); +#endif