liblcf
ini.cpp
Go to the documentation of this file.
1 /* inih -- simple .INI file parser
2 
3 The "inih" library is distributed under the New BSD license:
4 
5 Copyright (c) 2009, Ben Hoyt
6 All rights reserved.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10  * Redistributions of source code must retain the above copyright
11  notice, this list of conditions and the following disclaimer.
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in the
14  documentation and/or other materials provided with the distribution.
15  * Neither the name of Ben Hoyt nor the names of its contributors
16  may be used to endorse or promote products derived from this software
17  without specific prior written permission.
18 
19 THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
20 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
23 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 Go to the project home page for more info: https://github.com/benhoyt/inih
31 
32 */
33 
34 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
35 #define _CRT_SECURE_NO_WARNINGS
36 #endif
37 
38 #include <cstdio>
39 #include <cctype>
40 #include <cstring>
41 
42 #include "ini.h"
43 
44 #if !INI_USE_STACK
45 #include <cstdlib>
46 #endif
47 
48 #define MAX_SECTION 50
49 #define MAX_NAME 50
50 
51 /* Used by ini_parse_string() to keep track of string parsing state. */
52 typedef struct {
53  const char* ptr;
54  size_t num_left;
56 
57 /* Strip whitespace chars off end of given string, in place. Return s. */
58 static char* rstrip(char* s)
59 {
60  char* p = s + strlen(s);
61  while (p > s && isspace((unsigned char)(*--p)))
62  *p = '\0';
63  return s;
64 }
65 
66 /* Return pointer to first non-whitespace char in given string. */
67 static char* lskip(const char* s)
68 {
69  while (*s && isspace((unsigned char)(*s)))
70  s++;
71  return (char*)s;
72 }
73 
74 /* Return pointer to first char (of chars) or inline comment in given string,
75  or pointer to null at end of string if neither found. Inline comment must
76  be prefixed by a whitespace character to register as a comment. */
77 static char* find_chars_or_comment(const char* s, const char* chars)
78 {
79 #if INI_ALLOW_INLINE_COMMENTS
80  int was_space = 0;
81  while (*s && (!chars || !strchr(chars, *s)) &&
82  !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
83  was_space = isspace((unsigned char)(*s));
84  s++;
85  }
86 #else
87  while (*s && (!chars || !strchr(chars, *s))) {
88  s++;
89  }
90 #endif
91  return (char*)s;
92 }
93 
94 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
95 static char* strncpy0(char* dest, const char* src, size_t size)
96 {
97  strncpy(dest, src, size - 1);
98  dest[size - 1] = '\0';
99  return dest;
100 }
101 
102 /* See documentation in header file. */
103 int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
104  void* user)
105 {
106  /* Uses a fair bit of stack (use heap instead if you need to) */
107 #if INI_USE_STACK
108  char line[INI_MAX_LINE];
109  int max_line = INI_MAX_LINE;
110 #else
111  char* line;
112  int max_line = INI_INITIAL_ALLOC;
113 #endif
114 #if INI_ALLOW_REALLOC && !INI_USE_STACK
115  char* new_line;
116  int offset;
117 #endif
118  char section[MAX_SECTION] = "";
119  char prev_name[MAX_NAME] = "";
120 
121  char* start;
122  char* end;
123  char* name;
124  char* value;
125  int lineno = 0;
126  int error = 0;
127 
128 #if !INI_USE_STACK
129  line = (char*)malloc(INI_INITIAL_ALLOC);
130  if (!line) {
131  return -2;
132  }
133 #endif
134 
135 #if INI_HANDLER_LINENO
136 #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
137 #else
138 #define HANDLER(u, s, n, v) handler(u, s, n, v)
139 #endif
140 
141  /* Scan through stream line by line */
142  while (reader(line, max_line, stream) != NULL) {
143 #if INI_ALLOW_REALLOC && !INI_USE_STACK
144  offset = strlen(line);
145  while (offset == max_line - 1 && line[offset - 1] != '\n') {
146  max_line *= 2;
147  if (max_line > INI_MAX_LINE)
148  max_line = INI_MAX_LINE;
149  new_line = realloc(line, max_line);
150  if (!new_line) {
151  free(line);
152  return -2;
153  }
154  line = new_line;
155  if (reader(line + offset, max_line - offset, stream) == NULL)
156  break;
157  if (max_line >= INI_MAX_LINE)
158  break;
159  offset += strlen(line + offset);
160  }
161 #endif
162 
163  lineno++;
164 
165  start = line;
166 #if INI_ALLOW_BOM
167  if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
168  (unsigned char)start[1] == 0xBB &&
169  (unsigned char)start[2] == 0xBF) {
170  start += 3;
171  }
172 #endif
173  start = lskip(rstrip(start));
174 
175  if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
176  /* Start-of-line comment */
177  }
178 #if INI_ALLOW_MULTILINE
179  else if (*prev_name && *start && start > line) {
180  /* Non-blank line with leading whitespace, treat as continuation
181  of previous name's value (as per Python configparser). */
182  if (!HANDLER(user, section, prev_name, start) && !error)
183  error = lineno;
184  }
185 #endif
186  else if (*start == '[') {
187  /* A "[section]" line */
188  end = find_chars_or_comment(start + 1, "]");
189  if (*end == ']') {
190  *end = '\0';
191  strncpy0(section, start + 1, sizeof(section));
192  *prev_name = '\0';
193  }
194  else if (!error) {
195  /* No ']' found on section line */
196  error = lineno;
197  }
198  }
199  else if (*start) {
200  /* Not a comment, must be a name[=:]value pair */
201  end = find_chars_or_comment(start, "=:");
202  if (*end == '=' || *end == ':') {
203  *end = '\0';
204  name = rstrip(start);
205  value = end + 1;
206 #if INI_ALLOW_INLINE_COMMENTS
207  end = find_chars_or_comment(value, NULL);
208  if (*end)
209  *end = '\0';
210 #endif
211  value = lskip(value);
212  rstrip(value);
213 
214  /* Valid name[=:]value pair found, call handler */
215  strncpy0(prev_name, name, sizeof(prev_name));
216  if (!HANDLER(user, section, name, value) && !error)
217  error = lineno;
218  }
219  else if (!error) {
220  /* No '=' or ':' found on name[=:]value line */
221  error = lineno;
222  }
223  }
224 
225 #if INI_STOP_ON_FIRST_ERROR
226  if (error)
227  break;
228 #endif
229  }
230 
231 #if !INI_USE_STACK
232  free(line);
233 #endif
234 
235  return error;
236 }
237 
238 /* See documentation in header file. */
239 int ini_parse_file(FILE* file, ini_handler handler, void* user)
240 {
241  return ini_parse_stream((ini_reader)fgets, file, handler, user);
242 }
243 
244 /* See documentation in header file. */
245 int ini_parse(const char* filename, ini_handler handler, void* user)
246 {
247  FILE* file;
248  int error;
249 
250  file = fopen(filename, "r");
251  if (!file)
252  return -1;
253  error = ini_parse_file(file, handler, user);
254  fclose(file);
255  return error;
256 }
257 
258 /* An ini_reader function to read the next line from a string buffer. This
259  is the fgets() equivalent used by ini_parse_string(). */
260 static char* ini_reader_string(char* str, int num, void* stream) {
262  const char* ctx_ptr = ctx->ptr;
263  size_t ctx_num_left = ctx->num_left;
264  char* strp = str;
265  char c;
266 
267  if (ctx_num_left == 0 || num < 2)
268  return NULL;
269 
270  while (num > 1 && ctx_num_left != 0) {
271  c = *ctx_ptr++;
272  ctx_num_left--;
273  *strp++ = c;
274  if (c == '\n')
275  break;
276  num--;
277  }
278 
279  *strp = '\0';
280  ctx->ptr = ctx_ptr;
281  ctx->num_left = ctx_num_left;
282  return str;
283 }
284 
285 /* See documentation in header file. */
286 int ini_parse_string(const char* string, ini_handler handler, void* user) {
288 
289  ctx.ptr = string;
290  ctx.num_left = strlen(string);
291  return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
292  user);
293 }
int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user)
Definition: ini.cpp:103
static char * ini_reader_string(char *str, int num, void *stream)
Definition: ini.cpp:260
char *(* ini_reader)(char *str, int num, void *stream)
Definition: ini.h:60
#define MAX_NAME
Definition: ini.cpp:49
static char * find_chars_or_comment(const char *s, const char *chars)
Definition: ini.cpp:77
#define INI_INITIAL_ALLOC
Definition: ini.h:142
size_t num_left
Definition: ini.cpp:54
#define INI_MAX_LINE
Definition: ini.h:129
static char * rstrip(char *s)
Definition: ini.cpp:58
#define MAX_SECTION
Definition: ini.cpp:48
const char * ptr
Definition: ini.cpp:53
int ini_parse_file(FILE *file, ini_handler handler, void *user)
Definition: ini.cpp:239
#define HANDLER(u, s, n, v)
#define INI_INLINE_COMMENT_PREFIXES
Definition: ini.h:118
static char * lskip(const char *s)
Definition: ini.cpp:67
#define INI_START_COMMENT_PREFIXES
Definition: ini.h:108
int ini_parse_string(const char *string, ini_handler handler, void *user)
Definition: ini.cpp:286
static char * strncpy0(char *dest, const char *src, size_t size)
Definition: ini.cpp:95
int(* ini_handler)(void *user, const char *section, const char *name, const char *value)
Definition: ini.h:55
int ini_parse(const char *filename, ini_handler handler, void *user)
Definition: ini.cpp:245