1 /* inih -- simple .INI file parser
2
3 inih is released under the New BSD license (see LICENSE.txt). Go to the project
4 home page for more info:
5
6 http://code.google.com/p/inih/
7
8 */
9
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <string.h>
13
14 #include "inih.h"
15
16 #define MAX_LINE 1024
17 #define MAX_SECTION 64
18 #define MAX_NAME 64
19
20 /* Strip whitespace chars off end of given string, in place. Return s. */
rstrip(char * s)21 static char* rstrip(char* s)
22 {
23 char* p = s + strlen(s);
24 while (p > s && isspace(*--p))
25 *p = '\0';
26 return s;
27 }
28
29 /* Return pointer to first non-whitespace char in given string. */
lskip(const char * s)30 static char* lskip(const char* s)
31 {
32 while (*s && isspace(*s))
33 s++;
34 return (char*)s;
35 }
36
37 /* Return pointer to first char c or ';' comment in given string, or pointer to
38 null at end of string if neither found. ';' must be prefixed by a whitespace
39 character to register as a comment. */
find_char_or_comment(const char * s,char c)40 static char* find_char_or_comment(const char* s, char c)
41 {
42 int was_whitespace = 0;
43 while (*s && *s != c && !(was_whitespace && *s == ';')) {
44 was_whitespace = isspace(*s);
45 s++;
46 }
47 return (char*)s;
48 }
49
50 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
strncpy0(char * dest,const char * src,size_t size)51 static char* strncpy0(char* dest, const char* src, size_t size)
52 {
53 strncpy(dest, src, size);
54 dest[size - 1] = '\0';
55 return dest;
56 }
57
58 /* See documentation in header file. */
ini_parse_file(FILE * file,int (* handler)(void *,const char *,const char *,const char *),void * user)59 int ini_parse_file(FILE* file,
60 int (*handler)(void*, const char*, const char*,
61 const char*),
62 void* user)
63 {
64 /* Uses a fair bit of stack (use heap instead if you need to) */
65 char line[MAX_LINE];
66 char section[MAX_SECTION] = "";
67 char prev_name[MAX_NAME] = "";
68
69 char* start;
70 char* end;
71 char* name;
72 char* value;
73 int lineno = 0;
74 int error = 0;
75
76 /* Scan through file line by line */
77 while (fgets(line, sizeof(line), file) != NULL) {
78 lineno++;
79 start = lskip(rstrip(line));
80
81 if (*start == ';' || *start == '#') {
82 /* Per Python ConfigParser, allow '#' comments at start of line */
83 }
84 #if INI_ALLOW_MULTILINE
85 else if (*prev_name && *start && start > line) {
86 /* Non-black line with leading whitespace, treat as continuation
87 of previous name's value (as per Python ConfigParser). */
88 if (!handler(user, section, prev_name, start) && !error)
89 error = lineno;
90 }
91 #endif
92 else if (*start == '[') {
93 /* A "[section]" line */
94 end = find_char_or_comment(start + 1, ']');
95 if (*end == ']') {
96 *end = '\0';
97 strncpy0(section, start + 1, sizeof(section));
98 *prev_name = '\0';
99 }
100 else if (!error) {
101 /* No ']' found on section line */
102 error = lineno;
103 }
104 }
105 else if (*start && *start != ';') {
106 /* Not a comment, must be a name[=:]value pair */
107 end = find_char_or_comment(start, '=');
108 if (*end != '=') {
109 end = find_char_or_comment(start, ':');
110 }
111 if (*end == '=' || *end == ':') {
112 *end = '\0';
113 name = rstrip(start);
114 value = lskip(end + 1);
115 end = find_char_or_comment(value, '\0');
116 if (*end == ';')
117 *end = '\0';
118 rstrip(value);
119
120 /* Valid name[=:]value pair found, call handler */
121 strncpy0(prev_name, name, sizeof(prev_name));
122 if (!handler(user, section, name, value) && !error)
123 error = lineno;
124 }
125 else if (!error) {
126 /* No '=' or ':' found on name[=:]value line */
127 error = lineno;
128 }
129 }
130 }
131
132 return error;
133 }
134
135 /* See documentation in header file. */
ini_parse(const char * filename,int (* handler)(void *,const char *,const char *,const char *),void * user)136 int ini_parse(const char* filename,
137 int (*handler)(void*, const char*, const char*, const char*),
138 void* user)
139 {
140 FILE* file;
141 int error;
142
143 file = fopen(filename, "r");
144 if (!file)
145 return -1;
146 error = ini_parse_file(file, handler, user);
147 fclose(file);
148 return error;
149 }
150