1 /* SPDX-License-Identifier: Zlib */
2
3 #include <stdlib.h>
4 #include <glib.h>
5 #include <glib/gi18n-lib.h>
6 #include <string.h>
7 #ifdef WITH_JSON
8 #include <json.h>
9 #endif
10
11 #include "settings.h"
12 #include "datastructures.h"
13 #include "completion.h"
14 #include "session.h"
15 #include "internal.h"
16 #include "utils.h"
17
18 /**
19 * Structure of a settings entry
20 */
21 struct girara_setting_s
22 {
23 char* name; /**< Name of the setting */
24 union
25 {
26 bool b; /**< Boolean */
27 int i; /**< Integer */
28 float f; /**< Floating number */
29 char *s; /**< String */
30 } value; /**< Value of the setting */
31 girara_setting_type_t type; /**< Type identifier */
32 bool init_only; /**< Option can be set only before girara gets initialized */
33 char* description; /**< Description of this setting */
34 girara_setting_callback_t callback; /**< Callback that gets executed when the value of the setting changes */
35 void* data; /**< Arbitrary data that can be used by callbacks */
36 };
37
38 void
girara_setting_set_value(girara_session_t * session,girara_setting_t * setting,const void * value)39 girara_setting_set_value(girara_session_t* session, girara_setting_t* setting, const void* value)
40 {
41 g_return_if_fail(setting && (value || setting->type == STRING));
42
43 switch(setting->type) {
44 case BOOLEAN:
45 setting->value.b = *((const bool *) value);
46 break;
47 case FLOAT:
48 setting->value.f = *((const float *) value);
49 break;
50 case INT:
51 setting->value.i = *((const int *) value);
52 break;
53 case STRING:
54 if (setting->value.s != NULL) {
55 g_free(setting->value.s);
56 }
57 setting->value.s = value ? g_strdup(value) : NULL;
58 break;
59 default:
60 g_assert(false);
61 }
62
63 if (session && setting->callback != NULL) {
64 setting->callback(session, setting->name, setting->type, value, setting->data);
65 }
66 }
67
68 bool
girara_setting_add(girara_session_t * session,const char * name,const void * value,girara_setting_type_t type,bool init_only,const char * description,girara_setting_callback_t callback,void * data)69 girara_setting_add(girara_session_t* session, const char* name, const void* value, girara_setting_type_t type, bool init_only, const char* description, girara_setting_callback_t callback, void* data)
70 {
71 g_return_val_if_fail(session != NULL, false);
72 g_return_val_if_fail(name != NULL, false);
73 g_return_val_if_fail(type != UNKNOWN, false);
74 if (type != STRING && value == NULL) {
75 return false;
76 }
77
78 /* search for existing setting */
79 if (girara_setting_find(session, name) != NULL) {
80 return false;
81 }
82
83 /* add new setting */
84 girara_setting_t* setting = g_slice_new0(girara_setting_t);
85
86 setting->name = g_strdup(name);
87 setting->type = type;
88 setting->init_only = init_only;
89 setting->description = description ? g_strdup(description) : NULL;
90 setting->callback = callback;
91 setting->data = data;
92 girara_setting_set_value(NULL, setting, value);
93
94 girara_list_append(session->private_data->settings, setting);
95
96 return true;
97 }
98
99 bool
girara_setting_set(girara_session_t * session,const char * name,const void * value)100 girara_setting_set(girara_session_t* session, const char* name, const void* value)
101 {
102 g_return_val_if_fail(session != NULL, false);
103 g_return_val_if_fail(name != NULL, false);
104
105 girara_setting_t* setting = girara_setting_find(session, name);
106 if (setting == NULL) {
107 return false;
108 }
109
110 girara_setting_set_value(session, setting, value);
111 return true;
112 }
113
114 bool
girara_setting_get_value(girara_setting_t * setting,void * dest)115 girara_setting_get_value(girara_setting_t* setting, void* dest)
116 {
117 g_return_val_if_fail(setting != NULL && dest != NULL, false);
118
119 switch(setting->type) {
120 case BOOLEAN:
121 {
122 bool *bvalue = (bool *)dest;
123 *bvalue = setting->value.b;
124 break;
125 }
126 case FLOAT:
127 {
128 float *fvalue = (float *)dest;
129 *fvalue = setting->value.f;
130 break;
131 }
132 case INT:
133 {
134 int *ivalue = (int*) dest;
135 *ivalue = setting->value.i;
136 break;
137 }
138 case STRING:
139 {
140 char **svalue = (char**) dest;
141 *svalue = setting->value.s ? g_strdup(setting->value.s) : NULL;
142 break;
143 }
144 default:
145 g_assert(false);
146 }
147
148 return true;
149 }
150
151 bool
girara_setting_get(girara_session_t * session,const char * name,void * dest)152 girara_setting_get(girara_session_t* session, const char* name, void* dest)
153 {
154 g_return_val_if_fail(session != NULL && name != NULL && dest != NULL, false);
155
156 girara_setting_t* setting = girara_setting_find(session, name);
157 if (setting == NULL) {
158 return false;
159 }
160
161 return girara_setting_get_value(setting, dest);
162 }
163
164 void
girara_setting_free(girara_setting_t * setting)165 girara_setting_free(girara_setting_t* setting)
166 {
167 if (!setting) {
168 return;
169 }
170
171 g_free(setting->name);
172 g_free(setting->description);
173 if (setting->type == STRING) {
174 g_free(setting->value.s);
175 }
176 g_slice_free(girara_setting_t, setting);
177 }
178
179 girara_setting_t*
girara_setting_find(girara_session_t * session,const char * name)180 girara_setting_find(girara_session_t* session, const char* name)
181 {
182 g_return_val_if_fail(session != NULL, NULL);
183 g_return_val_if_fail(name != NULL, NULL);
184
185 girara_setting_t* result = NULL;
186 GIRARA_LIST_FOREACH_BODY(session->private_data->settings, girara_setting_t*, setting,
187 if (g_strcmp0(setting->name, name) == 0) {
188 result = setting;
189 break;
190 }
191 );
192
193 return result;
194 }
195
196 const char*
girara_setting_get_name(const girara_setting_t * setting)197 girara_setting_get_name(const girara_setting_t* setting) {
198 g_return_val_if_fail(setting, NULL);
199 return setting->name;
200 }
201
202 girara_setting_type_t
girara_setting_get_type(girara_setting_t * setting)203 girara_setting_get_type(girara_setting_t* setting) {
204 g_return_val_if_fail(setting, UNKNOWN);
205 return setting->type;
206 }
207
208 girara_completion_t*
girara_cc_set(girara_session_t * session,const char * input)209 girara_cc_set(girara_session_t* session, const char* input)
210 {
211 if (input == NULL) {
212 return NULL;
213 }
214
215 girara_completion_t* completion = girara_completion_init();
216 if (completion == NULL) {
217 return NULL;
218 }
219 girara_completion_group_t* group = girara_completion_group_create(session, NULL);
220 if (group == NULL) {
221 girara_completion_free(completion);
222 return NULL;
223 }
224 girara_completion_add_group(completion, group);
225
226 unsigned int input_length = strlen(input);
227
228 GIRARA_LIST_FOREACH_BODY(session->private_data->settings, girara_setting_t*, setting,
229 if ((setting->init_only == false) && (input_length <= strlen(setting->name)) &&
230 !strncmp(input, setting->name, input_length)) {
231 girara_completion_group_add_element(group, setting->name, setting->description);
232 }
233 );
234
235 return completion;
236 }
237
238 #ifdef WITH_JSON
239 bool
girara_cmd_dump_config(girara_session_t * session,girara_list_t * argument_list)240 girara_cmd_dump_config(girara_session_t* session, girara_list_t* argument_list)
241 {
242 if (session == NULL || argument_list == NULL) {
243 return false;
244 }
245
246 const size_t number_of_arguments = girara_list_size(argument_list);
247 if (number_of_arguments != 1) {
248 girara_warning("Invalid number of arguments passed: %zu instead of 1",
249 number_of_arguments);
250 girara_notify(session, GIRARA_ERROR,
251 _("Invalid number of arguments passed: %zu instead of 1"),
252 number_of_arguments);
253 return false;
254 }
255
256 json_object* json_config = json_object_new_object();
257
258 GIRARA_LIST_FOREACH_BODY(session->private_data->settings, girara_setting_t*, setting,
259 json_object* json_setting = json_object_new_object();
260
261 json_object* json_value = NULL;
262 json_object* json_type = NULL;
263 switch(setting->type) {
264 case BOOLEAN:
265 json_value = json_object_new_boolean(setting->value.b);
266 json_type = json_object_new_string("boolean");
267 break;
268 case FLOAT:
269 json_value = json_object_new_double(setting->value.f);
270 json_type = json_object_new_string("float");
271 break;
272 case INT:
273 json_value = json_object_new_int(setting->value.i);
274 json_type = json_object_new_string("int");
275 break;
276 case STRING:
277 json_value = json_object_new_string(setting->value.s ? setting->value.s : "");
278 json_type = json_object_new_string("string");
279 break;
280 default:
281 girara_debug("Invalid setting: %s", setting->name);
282 }
283 if (json_value != NULL) {
284 json_object_object_add(json_setting, "value", json_value);
285 json_object_object_add(json_setting, "type", json_type);
286 }
287
288 if (setting->description != NULL) {
289 json_object_object_add(json_setting, "description",
290 json_object_new_string(setting->description));
291 }
292 json_object_object_add(json_setting, "init-only",
293 json_object_new_boolean(setting->init_only));
294
295 json_object_object_add(json_config, setting->name, json_setting);
296 );
297
298 json_object_to_file_ext(girara_list_nth(argument_list, 0), json_config,
299 JSON_C_TO_STRING_PRETTY);
300 json_object_put(json_config);
301
302 return true;
303 }
304 #endif
305