Fun API Documentation 0.42.1
The programming language that makes you have fun!
Loading...
Searching...
No Matches
ini.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2025 Johannes Findeisen <you@hanez.org>
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
10/**
11 * @file ini.c
12 * @brief iniparser helpers for Fun VM INI-related opcodes (conditional build).
13 *
14 * This module centralizes small utilities and a tiny handle registry used by
15 * VM opcodes that interact with INI configuration files through the
16 * iniparser library. Placing the concrete logic in src/extensions/ keeps the
17 * opcode implementations minimal — they focus on VM stack marshalling and
18 * delegate the concrete work here, mirroring other extensions (PCRE2, SQLite, XML2).
19 *
20 * Build-time feature flag:
21 * - All code in this file is compiled only when FUN_WITH_INI is enabled.
22 * When disabled, INI-related opcodes should provide safe no-op fallbacks
23 * in their respective VM files.
24 *
25 * Registry and ownership model:
26 * - A very small fixed-size registry (g_ini) maps small positive integers to
27 * iniparser dictionaries. The registry OWNS the dictionary pointer and will
28 * call iniparser_freedict() when a handle is freed via ini_free_handle().
29 * - Handles are in the range [1, 63]; 0 indicates failure/invalid.
30 * - The registry does not perform I/O; callers are responsible for creating
31 * the dictionary (e.g., iniparser_load()) prior to registration.
32 *
33 * Thread-safety:
34 * - Not thread-safe. If used from multiple threads, coordinate access
35 * externally.
36 *
37 * Key formatting helper:
38 * - ini_make_full_key() produces a section-qualified key of the form
39 * "section:key", which is what iniparser expects for lookups.
40 */
41
42#ifdef FUN_WITH_INI
43#if defined(__has_include)
44#if __has_include(<iniparser/iniparser.h>)
45#include <iniparser/dictionary.h>
46#include <iniparser/iniparser.h>
47#elif __has_include(<iniparser.h>)
48#include <dictionary.h>
49#include <iniparser.h>
50#else
51#error "iniparser headers not found"
52#endif
53#else
54#include <iniparser/dictionary.h>
55#include <iniparser/iniparser.h>
56#endif
57#include <ctype.h>
58#include <stdio.h>
59#include <string.h>
60#include <stddef.h>
61
62/**
63 * @brief One slot in the global INI handle registry.
64 *
65 * Associates an iniparser dictionary pointer with an in-use flag. The
66 * registry takes ownership of the dictionary for the lifetime of the slot and
67 * will free it when the handle is released via ini_free_handle().
68 */
69typedef struct {
70 dictionary *dict;
71 int in_use;
72} IniSlot;
73
74/**
75 * @brief Fixed-size registry of iniparser dictionaries.
76 *
77 * Index 0 is reserved and never used for valid handles. Valid handles are in
78 * the range [1, 63]. When an entry's in_use flag is 0, the slot is available.
79 */
81
82/**
83 * @brief Allocate a registry handle for a newly created dictionary.
84 *
85 * Transfers ownership of the dictionary pointer to the registry on success.
86 *
87 * @param d Pointer to an initialized iniparser dictionary (e.g., from
88 * iniparser_load()). Must not be NULL.
89 * @return int Positive handle (>0) on success; 0 if no free slot is available
90 * or if d is NULL.
91 */
92int ini_alloc_handle(dictionary *d) {
93 if (!d) return 0;
94 for (int i = 1; i < (int)(sizeof(g_ini) / sizeof(g_ini[0])); ++i) {
95 if (!g_ini[i].in_use) {
96 g_ini[i].in_use = 1;
97 g_ini[i].dict = d;
98 return i;
99 }
100 }
101 return 0;
102}
103
104/**
105 * @brief Look up a dictionary pointer by registry handle.
106 *
107 * The returned pointer is owned by the registry; callers must not free it
108 * directly. Use ini_free_handle() to release the association.
109 *
110 * @param h Handle id previously returned by ini_alloc_handle().
111 * @return dictionary* Pointer to dictionary if the handle is valid and in use;
112 * NULL otherwise.
113 */
114dictionary *ini_get(int h) {
115 if (h > 0 && h < (int)(sizeof(g_ini) / sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
116 return NULL;
117}
118
119/**
120 * @brief Free a previously allocated handle and close its dictionary.
121 *
122 * If the slot holds a dictionary, iniparser_freedict() is called. The slot is
123 * then marked available for reuse.
124 *
125 * @param h Handle id to free.
126 * @return int 1 on success; 0 if the handle is invalid or not in use.
127 */
128int ini_free_handle(int h) {
129 if (h <= 0 || h >= (int)(sizeof(g_ini) / sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
130 if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
131 g_ini[h].dict = NULL;
132 g_ini[h].in_use = 0;
133 return 1;
134}
135
136/**
137 * @brief Build a fully qualified key of the form "section:key".
138 *
139 * Writes into a caller-provided buffer a key qualified by section, as expected
140 * by iniparser lookups. If sec is NULL, an empty section is used. If key is
141 * NULL, an empty key is used. The function is a no-op if buf is NULL or cap is
142 * 0. Output is always NUL-terminated (subject to snprintf semantics).
143 *
144 * @param buf Destination buffer.
145 * @param cap Capacity of buf in bytes (including terminator).
146 * @param sec Section name (may be NULL for default/empty section).
147 * @param key Key name (may be NULL to produce an empty key).
148 */
149void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key) {
150 if (!buf || cap == 0) return;
151 if (!sec) sec = "";
152 if (!key) key = "";
153 /* iniparser expects section:key; lookup is case-insensitive internally */
154 snprintf(buf, cap, "%s:%s", sec, key);
155}
156
157#endif /* FUN_WITH_INI */
dictionary * ini_get(int h)
Look up a dictionary pointer by registry handle.
Definition ini.c:114
int ini_free_handle(int h)
Free a previously allocated handle and close its dictionary.
Definition ini.c:128
int ini_alloc_handle(dictionary *d)
Allocate a registry handle for a newly created dictionary.
Definition ini.c:92
void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key)
Build a fully qualified key of the form "section:key".
Definition ini.c:149
IniSlot g_ini[64]
Fixed-size registry of iniparser dictionaries.
Definition ini.c:80
One slot in the global INI handle registry.
Definition ini.c:69
dictionary * dict
Definition ini.c:70
int in_use
Definition ini.c:71