ini

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

ini.c (4425B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <ctype.h>
      4 #include <string.h>
      5 #include "ini.h"
      6 
      7 internal int read_until(FILE* fp, char* buffer, int size, int delim);
      8 
      9 internal int callback(char* section, char* config, char* value, smallDictt *r)
     10 {
     11 	//printf("[%s] \"%s\" == \"%s\"\n", section, config, value);
     12 	if (isBlankG(section)) {
     13 		setG(r, config, value);
     14 	}
     15 	else {
     16 		smallDictt *sect = getG(r, rtSmallDictt, section);
     17 		if (sect) {
     18 			setG(sect, config, value);
     19 			setPG(r, section, sect);
     20 		}
     21 		else {
     22 			sect = allocG(rtSmallDictt);
     23 			setG(sect, config, value);
     24 			setNFreeG(r, section, sect);
     25 		}
     26 	}
     27 	return 0;
     28 }
     29 
     30 /**
     31  * @brief Parse an ini file.
     32  * @details Parse an ini file. Passes values to a callback function.
     33  * No dynamic memory is allocated.
     34  *
     35  * @param fp Open file pointer to read INI file from
     36  * @param callback Callback function accepting three arguments:
     37  * section, variable, and value strings.
     38  * @return 0 on success, error code on failure
     39  */
     40 internal int iniParseFile(FILE* fp, int (*callback)(char*, char*, char*, smallDictt *), smallDictt *r)
     41 {
     42 	if ((fp == NULL) || (callback == NULL))
     43 		return -1;
     44 
     45 	char section [BUFFER_SIZE] = "";
     46 	char config  [BUFFER_SIZE] = "";
     47 	char value   [BUFFER_SIZE] = "";
     48 	int c;
     49 	int err;
     50 	while((c = getc(fp)) != EOF) {
     51 		switch(c) {
     52 			case '#': {
     53 				while ((c = getc(fp)) != '\n')
     54 					;
     55 				break;
     56 			}
     57 			case '[': {
     58 				err = read_until(fp, section, BUFFER_SIZE, ']');
     59 				if (err)
     60 					return err;
     61 				break;
     62 			}
     63 			case '=': {
     64 				err = read_until(fp, value, BUFFER_SIZE, '\n');
     65 				if (err)
     66 					return err;
     67 				err = callback(section, config, value, r);
     68 				if (err)
     69 					return err;
     70 				break;
     71 			}
     72 			case '\n': case ' ': case '\t':
     73 				break;
     74 			default:
     75 				ungetc(c, fp);
     76 				err = read_until(fp, config, BUFFER_SIZE, '=');
     77 				if (err)
     78 					return err;
     79 				break;
     80 		}
     81 	}
     82 	return -1;
     83 }
     84 
     85 smallDictt *parseIni(const char *filename)
     86 {
     87 	FILE* fp = fopen(filename, "r");
     88 	pTestErrorCmd(fp == NULL, return NULL);
     89 
     90 	createAllocateSmallDict(r);
     91 	iniParseFile(fp, callback, r);
     92 	fclose(fp);
     93 	return r;
     94 }
     95 
     96 internal int read_until(FILE* fp, char* buffer, int size, int delim)
     97 {
     98 	char c;
     99 	int i = 0;
    100 	memset(buffer, 0, size);
    101 	int string = 0;
    102 	while (i < size) {
    103 		c = getc(fp);
    104 		if(isblank(c) && string == 0) {
    105 			continue;
    106 		} else if (c == '\"') {
    107 			string = 1;
    108 		} else if (c == delim) {
    109 			buffer[i] = '\0';
    110 			if (delim == '=')
    111 				ungetc(c, fp);
    112 			return 0;
    113 		} else if (c == EOF) {
    114 			return 0;
    115 		}
    116 		buffer[i++] = c;
    117 	}
    118 	buffer[i] = '\0';
    119 	return -1;
    120 }
    121 
    122 /**
    123  * @brief Parse an ini file.
    124  * @details Parse an ini file. Passes values to a callback function.
    125  * No dynamic memory is allocated.
    126  *
    127  * @param fp Open file pointer to read INI file from
    128  * @param callback Callback function accepting three arguments:
    129  * section, variable, and value strings.
    130  * @return 0 on success, error code on failure
    131  */
    132 int ini_parse_file(FILE* fp, int (*callback)(char*, char*, char*))
    133 {
    134 	if ((fp == NULL) || (callback == NULL))
    135 		return -1;
    136 
    137 	char section [BUFFER_SIZE] = "";
    138 	char config  [BUFFER_SIZE] = "";
    139 	char value   [BUFFER_SIZE] = "";
    140 	int c;
    141 	int err;
    142 	while((c = getc(fp)) != EOF) {
    143 		switch(c) {
    144 			case '#': {
    145 				while ((c = getc(fp)) != '\n')
    146 					;
    147 				break;
    148 			}
    149 			case '[': {
    150 				err = read_until(fp, section, BUFFER_SIZE, ']');
    151 				if (err)
    152 					return err;
    153 				break;
    154 			}
    155 			case '=': {
    156 				err = read_until(fp, value, BUFFER_SIZE, '\n');
    157 				if (err)
    158 					return err;
    159 				err = callback(section, config, value);
    160 				if (err)
    161 					return err;
    162 				break;
    163 			}
    164 			case '\n': case ' ': case '\t':
    165 				break;
    166 			default:
    167 				ungetc(c, fp);
    168 				err = read_until(fp, config, BUFFER_SIZE, '=');
    169 				if (err)
    170 					return err;
    171 				break;
    172 		}
    173 	}
    174 	return -1;
    175 }
    176 
    177 bool saveIni(smallDictt *ini, const char *path) {
    178 	if (!ini || !path) {
    179 		return false;
    180 	}
    181 	createAllocateSmallArray(r);
    182 	forEachSmallDict(ini, k, D) {
    183 		cast(smallDictt*, d, D);
    184 		pushNFreeG(r, catS("[", k, "]"));
    185 		{forEachSmallDict(d, k2, S) {
    186 			castS(s, S);
    187 			if (isBlankG(s)) {
    188 				pushNFreeG(r, catS(k2, " ="));
    189 			}
    190 			else {
    191 				pushNFreeG(r, catS(k2, " = ", ssGet(s)));
    192 			}
    193 			finishG(s);
    194 		}
    195 		listFreeS(libsheepyInternalKeys);
    196 		}
    197 		pushG(r, "");
    198 		finishG(d);
    199 	}
    200 	listFreeS(libsheepyInternalKeys);
    201 	writeFileG(r, path);
    202 	return true;
    203 }
    204 
    205 
    206 bool checkLibsheepyVersionIni(const char *currentLibsheepyVersion) {
    207   return eqG(currentLibsheepyVersion, LIBSHEEPY_VERSION);
    208 }
    209