Skip to content

Commit caef552

Browse files
committed
Safety improvements, better comment parsing, escaped character support, empty value support
1 parent 0779a16 commit caef552

File tree

2 files changed

+102
-22
lines changed

2 files changed

+102
-22
lines changed

ini_parser.h

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ extern "C" {
2424
#define INI_MAX_LINE_LENGTH 256
2525
#endif
2626

27+
#ifdef INI_ENABLE_CASE_SENSITIVITY
28+
#define STRCOMPARE(x, y) strcmp(x, y)
29+
#else
30+
#define STRCOMPARE(x, y) strcasecmp(x, y)
31+
#endif
32+
33+
#define INI_ALLOW_EMPTY_VALUES
34+
2735
typedef enum
2836
{
2937
INI_LINE_EMPTY,
@@ -93,8 +101,51 @@ static void trimWhitespace(char *str)
93101
end--;
94102
}
95103

96-
memmove(str, start, end - start + 1);
97-
str[end - start + 1] = '\0';
104+
size_t len = (end >= start) ? (end - start + 1) : 0;
105+
106+
if(len > 0)
107+
{
108+
memmove(str, start, len);
109+
}
110+
111+
str[len] = '\0';
112+
}
113+
114+
static void unescapeString(char *str)
115+
{
116+
char *src = str;
117+
char *dst = str;
118+
119+
while(*src)
120+
{
121+
if(*src == '\\')
122+
{
123+
src++;
124+
125+
switch(*src)
126+
{
127+
case '=':
128+
*dst++ = '=';
129+
break;
130+
131+
case '\\':
132+
*dst++ = '\\';
133+
break;
134+
135+
default:
136+
*dst++ = '\\';
137+
*dst++ = *src;
138+
}
139+
}
140+
else
141+
{
142+
*dst++ = *src;
143+
}
144+
145+
src++;
146+
}
147+
148+
*dst = '\0';
98149
}
99150

100151
static ini_linetype_t parseLine(const char *line, char *section, char *key, char *value)
@@ -118,7 +169,7 @@ static ini_linetype_t parseLine(const char *line, char *section, char *key, char
118169
{
119170
const char *start = ++line;
120171

121-
while(*line && *line != ']')
172+
while(*line && *line != ']' && *line != '\n')
122173
{
123174
line++;
124175
}
@@ -153,7 +204,14 @@ static ini_linetype_t parseLine(const char *line, char *section, char *key, char
153204
keyLen = (keyLen < INI_MAX_LINE_LENGTH - 1) ? keyLen : INI_MAX_LINE_LENGTH - 1;
154205
memcpy(key, keyStart, keyLen);
155206
key[keyLen] = '\0';
207+
unescapeString(key);
156208
trimWhitespace(key);
209+
210+
if(key[0] == '\0')
211+
{
212+
return INI_LINE_INVALID;
213+
}
214+
157215
line++;
158216

159217
while(isspace((unsigned char)*line))
@@ -172,7 +230,23 @@ static ini_linetype_t parseLine(const char *line, char *section, char *key, char
172230
valueLen = (valueLen < INI_MAX_LINE_LENGTH - 1) ? valueLen : INI_MAX_LINE_LENGTH - 1;
173231
memcpy(value, valueStart, valueLen);
174232
value[valueLen] = '\0';
233+
unescapeString(value);
234+
char *comment = strpbrk(value, ";#");
235+
236+
if(comment)
237+
{
238+
*comment = '\0';
239+
}
240+
175241
trimWhitespace(value);
242+
#ifndef INI_ALLOW_EMPTY_VALUES
243+
244+
if(value[0] == '\0')
245+
{
246+
return INI_LINE_INVALID;
247+
}
248+
249+
#endif
176250
return INI_LINE_KEY_VALUE;
177251
}
178252

@@ -186,7 +260,7 @@ bool ini_initialize(ini_context_t *ctx, const char *content, size_t length)
186260
return false;
187261
}
188262

189-
ctx->content = malloc(length + 1);
263+
ctx->content = calloc(1, length + 1);
190264

191265
if(!ctx->content)
192266
{
@@ -195,6 +269,7 @@ bool ini_initialize(ini_context_t *ctx, const char *content, size_t length)
195269

196270
memcpy(ctx->content, content, length);
197271
ctx->content[length] = '\0';
272+
ctx->sections = NULL;
198273
ini_section_t *currentSection = NULL;
199274
char line[INI_MAX_LINE_LENGTH];
200275
const char *ptr = ctx->content;
@@ -225,7 +300,7 @@ bool ini_initialize(ini_context_t *ctx, const char *content, size_t length)
225300

226301
if(type == INI_LINE_SECTION)
227302
{
228-
ini_section_t *newSection = malloc(sizeof(ini_section_t));
303+
ini_section_t *newSection = calloc(1, sizeof(ini_section_t));
229304

230305
if(!newSection)
231306
{
@@ -257,16 +332,18 @@ bool ini_initialize(ini_context_t *ctx, const char *content, size_t length)
257332
}
258333
else if(type == INI_LINE_KEY_VALUE && currentSection)
259334
{
260-
ini_keyvalue_t *newKv = malloc(sizeof(ini_keyvalue_t));
335+
ini_keyvalue_t *newKv = calloc(1, sizeof(ini_keyvalue_t));
261336

262337
if(!newKv)
263338
{
264339
ini_cleanup(ctx);
265340
return false;
266341
}
267342

268-
strncpy(newKv->key, key, INI_MAX_LINE_LENGTH);
269-
strncpy(newKv->value, value, INI_MAX_LINE_LENGTH);
343+
strncpy(newKv->key, key, INI_MAX_LINE_LENGTH - 1);
344+
newKv->key[INI_MAX_LINE_LENGTH - 1] = '\0';
345+
strncpy(newKv->value, value, INI_MAX_LINE_LENGTH - 1);
346+
newKv->value[INI_MAX_LINE_LENGTH - 1] = '\0';
270347
newKv->next = NULL;
271348

272349
if(!currentSection->keyValues)
@@ -335,7 +412,7 @@ bool ini_hasSection(const ini_context_t *ctx, const char *section)
335412

336413
while(current)
337414
{
338-
if(strcasecmp(current->name, section) == 0)
415+
if(STRCOMPARE(current->name, section) == 0)
339416
{
340417
return true;
341418
}
@@ -357,13 +434,13 @@ bool ini_hasKey(const ini_context_t *ctx, const char *section, const char *key)
357434

358435
while(current)
359436
{
360-
if(strcasecmp(current->name, section) == 0)
437+
if(STRCOMPARE(current->name, section) == 0)
361438
{
362439
ini_keyvalue_t *kv = current->keyValues;
363440

364441
while(kv)
365442
{
366-
if(strcasecmp(kv->key, key) == 0)
443+
if(STRCOMPARE(kv->key, key) == 0)
367444
{
368445
return true;
369446
}
@@ -399,13 +476,13 @@ bool ini_getValue(const ini_context_t *ctx, const char *section, const char *key
399476

400477
while(current)
401478
{
402-
if(strcasecmp(current->name, section) == 0)
479+
if(STRCOMPARE(current->name, section) == 0)
403480
{
404481
ini_keyvalue_t *kv = current->keyValues;
405482

406483
while(kv)
407484
{
408-
if(strcasecmp(kv->key, key) == 0)
485+
if(STRCOMPARE(kv->key, key) == 0)
409486
{
410487
strncpy(value, kv->value, maxLen);
411488
value[maxLen - 1] = '\0';

ini_parser_tests.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ TEST_F(IniParserTest, HandlesWhitespace)
6262
"key2= \n";
6363
ASSERT_TRUE(LoadIniContent(content));
6464
EXPECT_TRUE(ini_hasSection(&ctx, "section1"));
65-
char value[256] = {0};
65+
char value[INI_MAX_LINE_LENGTH] = {0};
6666
EXPECT_TRUE(ini_getValue(&ctx, "section1", "key1", value, sizeof(value)));
6767
EXPECT_STREQ(value, "value1");
6868
EXPECT_TRUE(ini_hasKey(&ctx, "section1", "key2"));
@@ -82,9 +82,9 @@ TEST_F(IniParserTest, HandlesCommentsAndEmptyLines)
8282
ASSERT_TRUE(LoadIniContent(content));
8383
EXPECT_TRUE(ini_hasSection(&ctx, "section1"));
8484
EXPECT_TRUE(ini_hasSection(&ctx, "section2"));
85-
char value[256];
85+
char value[INI_MAX_LINE_LENGTH];
8686
EXPECT_TRUE(ini_getValue(&ctx, "section1", "key1", value, sizeof(value)));
87-
EXPECT_STREQ(value, "value1 ; inline comment");
87+
EXPECT_STREQ(value, "value1");
8888
}
8989

9090
TEST_F(IniParserTest, CaseInsensitivity)
@@ -95,7 +95,7 @@ TEST_F(IniParserTest, CaseInsensitivity)
9595
ASSERT_TRUE(LoadIniContent(content));
9696
EXPECT_TRUE(ini_hasSection(&ctx, "sEcTiOn1"));
9797
EXPECT_TRUE(ini_hasKey(&ctx, "SECTION1", "kEy1"));
98-
char value[256];
98+
char value[INI_MAX_LINE_LENGTH];
9999
EXPECT_TRUE(ini_getValue(&ctx, "section1", "KEY1", value, sizeof(value)));
100100
EXPECT_STREQ(value, "Value1");
101101
}
@@ -107,9 +107,12 @@ TEST_F(IniParserTest, HandlesSpecialCharacters)
107107
"key$%^=value&*()\n"
108108
"escaped_key=\"quoted value\"\n";
109109
ASSERT_TRUE(LoadIniContent(content));
110-
char value[256];
110+
char value[INI_MAX_LINE_LENGTH];
111+
EXPECT_TRUE(ini_hasSection(&ctx, "section!@#"));
112+
EXPECT_TRUE(ini_hasKey(&ctx, "section!@#", "key$%^"));
111113
EXPECT_TRUE(ini_getValue(&ctx, "section!@#", "key$%^", value, sizeof(value)));
112114
EXPECT_STREQ(value, "value&*()");
115+
EXPECT_TRUE(ini_hasKey(&ctx, "section!@#", "escaped_key"));
113116
EXPECT_TRUE(ini_getValue(&ctx, "section!@#", "escaped_key", value, sizeof(value)));
114117
EXPECT_STREQ(value, "\"quoted value\"");
115118
}
@@ -121,7 +124,7 @@ TEST_F(IniParserTest, HandlesDuplicateKeys)
121124
"key1=first\n"
122125
"key1=second\n";
123126
ASSERT_TRUE(LoadIniContent(content));
124-
char value[256];
127+
char value[INI_MAX_LINE_LENGTH];
125128
EXPECT_TRUE(ini_getValue(&ctx, "section1", "key1", value, sizeof(value)));
126129
EXPECT_STREQ(value, "second");
127130
}
@@ -160,7 +163,7 @@ TEST_F(IniParserTest, InvalidArguments)
160163
// Null section/key
161164
EXPECT_FALSE(ini_hasSection(&ctx, nullptr));
162165
EXPECT_FALSE(ini_hasKey(&ctx, "section1", nullptr));
163-
char value[256];
166+
char value[INI_MAX_LINE_LENGTH];
164167
EXPECT_FALSE(ini_getValue(&ctx, "section1", "key1", nullptr, sizeof(value)));
165168
EXPECT_FALSE(ini_getValue(&ctx, "section1", "key1", value, 0));
166169
}
@@ -177,7 +180,7 @@ TEST_F(IniParserTest, MalformedLines)
177180
ASSERT_TRUE(LoadIniContent(content));
178181
EXPECT_TRUE(ini_hasSection(&ctx, "section2"));
179182
EXPECT_FALSE(ini_hasSection(&ctx, "section1"));
180-
char value[256];
183+
char value[INI_MAX_LINE_LENGTH];
181184
EXPECT_TRUE(ini_getValue(&ctx, "section2", "key3", value, sizeof(value)));
182185
EXPECT_STREQ(value, "value3");
183186
}
@@ -205,7 +208,7 @@ TEST_F(IniParserTest, MultiSectionOperations)
205208
"[sectionB]\n"
206209
"key1=value2\n";
207210
ASSERT_TRUE(LoadIniContent(content));
208-
char value[256];
211+
char value[INI_MAX_LINE_LENGTH];
209212
EXPECT_TRUE(ini_getValue(&ctx, "sectionA", "key1", value, sizeof(value)));
210213
EXPECT_STREQ(value, "value1");
211214
EXPECT_TRUE(ini_getValue(&ctx, "sectionB", "key1", value, sizeof(value)));

0 commit comments

Comments
 (0)