Skip to content

Commit bdd0455

Browse files
authored
Merge pull request #8 from choco-technologies/copilot/add-active-section-support
Add active section restriction with owner-token protection to dmini context
2 parents e57b7bf + 3aed1ca commit bdd0455

3 files changed

Lines changed: 320 additions & 19 deletions

File tree

apps/test_dmini/test_dmini.c

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,107 @@ static void test_inline_comments(void)
334334
}
335335

336336
/**
337-
* @brief Main entry point for test application
337+
* @brief Test: Active section restriction
338338
*/
339+
static void test_active_section(void)
340+
{
341+
TEST_START("Active section restriction");
342+
343+
const char* ini_data =
344+
"global_key=global_value\n"
345+
"\n"
346+
"[driver1]\n"
347+
"param=hello\n"
348+
"\n"
349+
"[driver2]\n"
350+
"param=world\n";
351+
352+
/* --- unprotected context (token 0) --- */
353+
dmini_context_t ctx = dmini_create();
354+
TEST_ASSERT(ctx != NULL, "Failed to create context");
355+
356+
int result = dmini_parse_string(ctx, ini_data);
357+
TEST_ASSERT(result == DMINI_OK, "Failed to parse string");
358+
359+
/* Restrict to driver1 */
360+
result = dmini_set_active_section(ctx, "driver1", 0);
361+
TEST_ASSERT(result == DMINI_OK, "Failed to set active section");
362+
363+
/* NULL maps to active section */
364+
const char* val = dmini_get_string(ctx, NULL, "param", "");
365+
TEST_ASSERT(strcmp(val, "hello") == 0, "NULL should map to active section");
366+
367+
/* Active section is also accessible by name */
368+
val = dmini_get_string(ctx, "driver1", "param", "");
369+
TEST_ASSERT(strcmp(val, "hello") == 0, "Active section should be accessible by name");
370+
371+
/* Other sections are not visible */
372+
val = dmini_get_string(ctx, "driver2", "param", "not_found");
373+
TEST_ASSERT(strcmp(val, "not_found") == 0, "Other section should not be visible");
374+
375+
TEST_ASSERT(dmini_has_section(ctx, "driver2") == 0, "driver2 should not be visible");
376+
TEST_ASSERT(dmini_has_key(ctx, "driver2", "param") == 0, "key in hidden section invisible");
377+
378+
/* Clear restriction */
379+
result = dmini_clear_active_section(ctx, 0);
380+
TEST_ASSERT(result == DMINI_OK, "Failed to clear active section");
381+
382+
/* After clearing, all sections visible again */
383+
val = dmini_get_string(ctx, "driver2", "param", "");
384+
TEST_ASSERT(strcmp(val, "world") == 0, "driver2 should be visible after clearing");
385+
386+
dmini_destroy(ctx);
387+
388+
/* --- token-protected context --- */
389+
const unsigned int test_token = 0xCAFEBABEu;
390+
ctx = dmini_create_with_token(test_token);
391+
TEST_ASSERT(ctx != NULL, "Failed to create context with token");
392+
393+
result = dmini_parse_string(ctx, ini_data);
394+
TEST_ASSERT(result == DMINI_OK, "Failed to parse string");
395+
396+
/* Wrong token must be rejected */
397+
result = dmini_set_active_section(ctx, "driver1", test_token + 1);
398+
TEST_ASSERT(result == DMINI_ERR_LOCKED, "Wrong token should be rejected");
399+
400+
/* Correct token must succeed */
401+
result = dmini_set_active_section(ctx, "driver1", test_token);
402+
TEST_ASSERT(result == DMINI_OK, "Correct token should succeed");
403+
404+
val = dmini_get_string(ctx, NULL, "param", "");
405+
TEST_ASSERT(strcmp(val, "hello") == 0, "Active section accessible via NULL");
406+
407+
/* Wrong token must not clear restriction */
408+
result = dmini_clear_active_section(ctx, test_token + 1);
409+
TEST_ASSERT(result == DMINI_ERR_LOCKED, "Wrong token should not clear restriction");
410+
411+
/* Section is still restricted after failed clear */
412+
val = dmini_get_string(ctx, "driver2", "param", "not_found");
413+
TEST_ASSERT(strcmp(val, "not_found") == 0, "Restriction should still be active");
414+
415+
/* Correct token clears restriction */
416+
result = dmini_clear_active_section(ctx, test_token);
417+
TEST_ASSERT(result == DMINI_OK, "Correct token should clear restriction");
418+
419+
val = dmini_get_string(ctx, "driver2", "param", "");
420+
TEST_ASSERT(strcmp(val, "world") == 0, "All sections visible after clear");
421+
422+
/* Active section set to NULL (global section) */
423+
result = dmini_set_active_section(ctx, NULL, test_token);
424+
TEST_ASSERT(result == DMINI_OK, "Setting active section to NULL (global) should succeed");
425+
426+
val = dmini_get_string(ctx, NULL, "global_key", "");
427+
TEST_ASSERT(strcmp(val, "global_value") == 0, "Global section visible via NULL active section");
428+
429+
val = dmini_get_string(ctx, "driver1", "param", "not_found");
430+
TEST_ASSERT(strcmp(val, "not_found") == 0, "Named sections hidden when global is active");
431+
432+
dmini_clear_active_section(ctx, test_token);
433+
dmini_destroy(ctx);
434+
TEST_PASS();
435+
}
436+
437+
339438
int main(int argc, char** argv)
340439
{
341440
DMOD_LOG_INFO("=== DMINI Functionality Tests ===\n\n");
@@ -353,6 +452,7 @@ int main(int argc, char** argv)
353452
test_file_io();
354453
test_comments_whitespace();
355454
test_inline_comments();
455+
test_active_section();
356456

357457
// Print summary
358458
Dmod_Printf("\n=== Test Summary ===\n");

include/dmini.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define DMINI_ERR_INVALID -3
2727
#define DMINI_ERR_NOT_FOUND -4
2828
#define DMINI_ERR_FILE -5
29+
#define DMINI_ERR_LOCKED -6
2930

3031
/**
3132
* @brief INI context type (opaque)
@@ -207,4 +208,52 @@ dmod_dmini_api(1.0, int, _remove_key, (dmini_context_t ctx,
207208
const char* section,
208209
const char* key));
209210

211+
/**
212+
* @brief Initialize INI context with owner token
213+
*
214+
* Creates a new INI context protected by a magic number token.
215+
* The token must be supplied to dmini_set_active_section and
216+
* dmini_clear_active_section when a non-zero token is used.
217+
* Using token value 0 is equivalent to calling dmini_create().
218+
*
219+
* @param owner_token Magic number that guards active-section changes (0 = unprotected)
220+
* @return Pointer to INI context or NULL on error
221+
*/
222+
dmod_dmini_api(1.0, dmini_context_t, _create_with_token, (unsigned int owner_token));
223+
224+
/**
225+
* @brief Set the active section restriction
226+
*
227+
* Restricts the context so that only the named section is visible to
228+
* consumers of this context. While the restriction is active all API
229+
* calls treat section == NULL as a reference to the active section, and
230+
* any attempt to access a different section returns not-found / default.
231+
* Pass NULL as section to restrict to the global (unnamed) section.
232+
*
233+
* @param ctx INI context
234+
* @param section Section to make active (NULL for global section)
235+
* @param owner_token Magic number that was supplied to dmini_create_with_token
236+
* (ignored when the context was created with token 0)
237+
* @return DMINI_OK on success, DMINI_ERR_LOCKED if the token is wrong,
238+
* DMINI_ERR_INVALID if ctx is NULL
239+
*/
240+
dmod_dmini_api(1.0, int, _set_active_section, (dmini_context_t ctx,
241+
const char* section,
242+
unsigned int owner_token));
243+
244+
/**
245+
* @brief Clear the active section restriction
246+
*
247+
* Removes the active-section restriction so that the full content of the
248+
* context becomes visible again.
249+
*
250+
* @param ctx INI context
251+
* @param owner_token Magic number that was supplied to dmini_create_with_token
252+
* (ignored when the context was created with token 0)
253+
* @return DMINI_OK on success, DMINI_ERR_LOCKED if the token is wrong,
254+
* DMINI_ERR_INVALID if ctx is NULL
255+
*/
256+
dmod_dmini_api(1.0, int, _clear_active_section, (dmini_context_t ctx,
257+
unsigned int owner_token));
258+
210259
#endif // DMINI_H

0 commit comments

Comments
 (0)