| /* inih -- simple .INI file parser |
| |
| inih is released under the New BSD license (see LICENSE.txt). Go to the project |
| home page for more info: |
| |
| http://code.google.com/p/inih/ |
| |
| */ |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| |
| #include "ini.h" |
| |
| #define MAX_LINE 200 |
| #define MAX_SECTION 50 |
| #define MAX_NAME 50 |
| |
| /* Strip whitespace chars off end of given string, in place. Return s. */ |
| static char* rstrip(char* s) |
| { |
| char* p = s + strlen(s); |
| while (p > s && isspace(*--p)) |
| *p = '\0'; |
| return s; |
| } |
| |
| /* Return pointer to first non-whitespace char in given string. */ |
| static char* lskip(const char* s) |
| { |
| while (*s && isspace(*s)) |
| s++; |
| return (char*)s; |
| } |
| |
| /* Return pointer to first char c or ';' comment in given string, or pointer to |
| null at end of string if neither found. ';' must be prefixed by a whitespace |
| character to register as a comment. */ |
| static char* find_char_or_comment(const char* s, char c) |
| { |
| int was_whitespace = 0; |
| while (*s && *s != c && !(was_whitespace && *s == ';')) { |
| was_whitespace = isspace(*s); |
| s++; |
| } |
| return (char*)s; |
| } |
| |
| /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ |
| static char* strncpy0(char* dest, const char* src, size_t size) |
| { |
| strncpy(dest, src, size); |
| dest[size - 1] = '\0'; |
| return dest; |
| } |
| |
| /* See documentation in header file. */ |
| int ini_parse_file(FILE* file, |
| int (*handler)(void*, const char*, const char*, |
| const char*), |
| void* user) |
| { |
| /* Uses a fair bit of stack (use heap instead if you need to) */ |
| char line[MAX_LINE]; |
| char section[MAX_SECTION] = ""; |
| char prev_name[MAX_NAME] = ""; |
| |
| char* start; |
| char* end; |
| char* name; |
| char* value; |
| int lineno = 0; |
| int error = 0; |
| |
| /* Scan through file line by line */ |
| while (fgets(line, sizeof(line), file) != NULL) { |
| lineno++; |
| |
| start = line; |
| #if INI_ALLOW_BOM |
| if (lineno == 1 && start[0] == 0xEF && start[1] == 0xBB && |
| start[2] == 0xBF) { |
| start += 3; |
| } |
| #endif |
| start = lskip(rstrip(start)); |
| |
| if (*start == ';' || *start == '#') { |
| /* Per Python ConfigParser, allow '#' comments at start of line */ |
| } |
| #if INI_ALLOW_MULTILINE |
| else if (*prev_name && *start && start > line) { |
| /* Non-black line with leading whitespace, treat as continuation |
| of previous name's value (as per Python ConfigParser). */ |
| if (!handler(user, section, prev_name, start) && !error) |
| error = lineno; |
| } |
| #endif |
| else if (*start == '[') { |
| /* A "[section]" line */ |
| end = find_char_or_comment(start + 1, ']'); |
| if (*end == ']') { |
| *end = '\0'; |
| strncpy0(section, start + 1, sizeof(section)); |
| *prev_name = '\0'; |
| } |
| else if (!error) { |
| /* No ']' found on section line */ |
| error = lineno; |
| } |
| } |
| else if (*start && *start != ';') { |
| /* Not a comment, must be a name[=:]value pair */ |
| end = find_char_or_comment(start, '='); |
| if (*end != '=') { |
| end = find_char_or_comment(start, ':'); |
| } |
| if (*end == '=' || *end == ':') { |
| *end = '\0'; |
| name = rstrip(start); |
| value = lskip(end + 1); |
| end = find_char_or_comment(value, '\0'); |
| if (*end == ';') |
| *end = '\0'; |
| rstrip(value); |
| |
| /* Valid name[=:]value pair found, call handler */ |
| strncpy0(prev_name, name, sizeof(prev_name)); |
| if (!handler(user, section, name, value) && !error) |
| error = lineno; |
| } |
| else if (!error) { |
| /* No '=' or ':' found on name[=:]value line */ |
| error = lineno; |
| } |
| } |
| } |
| |
| return error; |
| } |
| |
| /* See documentation in header file. */ |
| int ini_parse(const char* filename, |
| int (*handler)(void*, const char*, const char*, const char*), |
| void* user) |
| { |
| FILE* file; |
| int error; |
| |
| file = fopen(filename, "r"); |
| if (!file) |
| return -1; |
| error = ini_parse_file(file, handler, user); |
| fclose(file); |
| return error; |
| } |