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