Remove unused variables.
[lxde/lxpanel.git] / src / conf.c
CommitLineData
0260eac5
AG
1/*
2 * Copyright (c) 2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include "conf.h"
944a8264 24#include "private.h"
0260eac5 25
17fab6e5
AG
26#include <string.h>
27#include <stdlib.h>
28
0260eac5
AG
29struct _config_setting_t
30{
31 config_setting_t *next;
32 config_setting_t *parent;
33 PanelConfType type;
34 PanelConfSaveHook hook;
17fab6e5 35 gpointer hook_data;
0260eac5
AG
36 char *name;
37 union {
38 gint num; /* for integer or boolean */
39 gchar *str; /* for string */
40 config_setting_t *first; /* for group or list */
41 };
42};
43
44struct _PanelConf
45{
46 config_setting_t *root;
47};
48
49static config_setting_t *_config_setting_t_new(config_setting_t *parent, int index,
50 const char *name, PanelConfType type)
51{
52 config_setting_t *s;
53 s = g_slice_new0(config_setting_t);
54 s->type = type;
55 s->name = g_strdup(name);
56 if (parent == NULL || (parent->type != PANEL_CONF_TYPE_GROUP && parent->type != PANEL_CONF_TYPE_LIST))
57 return s;
58 s->parent = parent;
59 if (parent->first == NULL || index == 0)
60 {
61 s->next = parent->first;
62 parent->first = s;
63 }
64 else
65 {
66 for (parent = parent->first; parent->next && index != 1; parent = parent->next)
67 index--;
68 /* FIXME: check if index is out of range? */
69 s->next = parent->next;
70 parent->next = s;
71 }
72 return s;
73}
74
75/* frees data, not removes from parent */
76static void _config_setting_t_free(config_setting_t *setting)
77{
78 g_free(setting->name);
79 switch (setting->type)
80 {
81 case PANEL_CONF_TYPE_STRING:
82 g_free(setting->str);
83 break;
84 case PANEL_CONF_TYPE_GROUP:
85 case PANEL_CONF_TYPE_LIST:
86 while (setting->first)
87 {
88 config_setting_t *s = setting->first;
89 setting->first = s->next;
90 _config_setting_t_free(s);
91 }
92 break;
93 case PANEL_CONF_TYPE_INT:
94 break;
95 }
96 g_slice_free(config_setting_t, setting);
97}
98
99/* the same as above but removes from parent */
100static void _config_setting_t_remove(config_setting_t *setting)
101{
102 g_return_if_fail(setting->parent);
103 g_return_if_fail(setting->parent->type == PANEL_CONF_TYPE_GROUP || setting->parent->type == PANEL_CONF_TYPE_LIST);
104 /* remove from parent */
105 if (setting->parent->first == setting)
106 setting->parent->first = setting->next;
107 else
108 {
109 config_setting_t *s = setting->parent->first;
110 while (s->next != NULL && s->next != setting)
111 s = s->next;
112 g_assert(s->next != NULL);
113 s->next = setting->next;
114 }
115 /* free the data */
116 _config_setting_t_free(setting);
117}
118
119PanelConf *config_new(void)
120{
121 PanelConf *c = g_slice_new(PanelConf);
122 c->root = _config_setting_t_new(NULL, -1, NULL, PANEL_CONF_TYPE_GROUP);
17fab6e5 123 return c;
0260eac5
AG
124}
125
126void config_destroy(PanelConf * config)
127{
128 _config_setting_t_free(config->root);
129 g_slice_free(PanelConf, config);
130}
131
132gboolean config_read_file(PanelConf * config, const char * filename)
133{
134 FILE *f = fopen(filename, "r");
050bf5bb 135 size_t size;
0260eac5
AG
136 char *buff, *c, *name, *end;
137 config_setting_t *s, *parent;
138
139 if (f == NULL)
140 return FALSE;
141 fseek(f, 0L, SEEK_END);
142 size = ftell(f);
143 rewind(f);
144 buff = g_malloc(size + 1);
050bf5bb 145 size = fread(buff, 1, size, f);
0260eac5
AG
146 fclose(f);
147 buff[size] = '\0';
148 name = NULL;
149 parent = config->root;
150 for (c = buff; *c; )
151 {
152 switch(*c)
153 {
154 case '#':
155_skip_all:
156 while (*c && *c != '\n')
157 c++;
158 if (!*c)
159 break;
160 /* continue with EOL */
161 case '\n':
162 name = NULL;
163 c++;
164 break;
165 case ' ':
166 case '\t':
167 if (name)
168 *c = '\0';
169 c++;
170 break;
171 case '=': /* scalar value follows */
172 if (name)
173 *c++ = '\0';
174 else
175 {
17fab6e5 176 g_warning("config: invalid scalar definition");
0260eac5
AG
177 goto _skip_all;
178 }
17fab6e5 179 while (*c == ' ' || *c == '\t')
0260eac5 180 c++; /* skip spaces after '=' */
17fab6e5 181 if (name == NULL || *c == '\0' || *c == '\n') /* invalid statement */
0260eac5
AG
182 break;
183 size = strtol(c, &end, 10);
17fab6e5 184 while (*end == ' ' || *end == '\t')
0260eac5
AG
185 c++; /* skip trailing spaces */
186 if (*end == '\0' || *end == '\n')
187 {
188 s = config_setting_add(parent, name, PANEL_CONF_TYPE_INT);
189 if (s)
17fab6e5 190 {
0260eac5 191 s->num = (int)size;
17fab6e5
AG
192 /* g_debug("config loader: got new int %s: %d", name, s->num); */
193 }
0260eac5 194 else
17fab6e5 195 g_warning("config: duplicate setting '%s' conflicts, ignored", name);
0260eac5
AG
196 }
197 else
198 {
199 for (end = c; *end && *end != '\n'; )
200 end++;
201 s = config_setting_add(parent, name, PANEL_CONF_TYPE_STRING);
202 if (s)
203 {
204 g_free(s->str);
205 s->str = g_strndup(c, end - c);
17fab6e5 206 /* g_debug("config loader: got new string %s: %s", name, s->str); */
0260eac5
AG
207 }
208 else
17fab6e5 209 g_warning("config: duplicate setting '%s' conflicts, ignored", name);
0260eac5
AG
210 }
211 c = end;
212 break;
213 case '{':
214 parent = config_setting_add(parent, "", PANEL_CONF_TYPE_LIST);
215 if (name)
216 {
217 *c = '\0';
218 s = config_setting_add(parent, name, PANEL_CONF_TYPE_GROUP);
219 }
220 else
221 s = NULL;
222 c++;
223 if (s)
224 {
225 parent = s;
17fab6e5 226 /* g_debug("config loader: group '%s' added", name); */
0260eac5
AG
227 }
228 else
17fab6e5 229 g_warning("config: invalid group '%s' in config file ignored", name);
0260eac5
AG
230 name = NULL;
231 break;
232 case '}':
233 c++;
234 if (parent->parent)
235 parent = parent->parent; /* go up, to anonymous list */
236 if (parent->type == PANEL_CONF_TYPE_LIST)
237 parent = parent->parent; /* go to upper group */
238 name = NULL;
239 break;
240 default:
241 if (name == NULL)
242 name = c;
243 c++;
244 }
245 }
246 g_free(buff);
247 return TRUE;
248}
249
250#define SETTING_INDENT " "
251
252static void _config_write_setting(const config_setting_t *setting, GString *buf,
253 GString *out, FILE *f)
254{
255 gint indent = buf->len;
256 config_setting_t *s;
257
258 switch (setting->type)
259 {
260 case PANEL_CONF_TYPE_INT:
261 g_string_append_printf(buf, "%s=%d\n", setting->name, setting->num);
262 break;
263 case PANEL_CONF_TYPE_STRING:
096e155f
AG
264 if (!setting->str) /* don't save NULL strings */
265 return;
266 g_string_append_printf(buf, "%s=%s\n", setting->name, setting->str);
0260eac5
AG
267 break;
268 case PANEL_CONF_TYPE_GROUP:
0260eac5 269 if (!out && setting->hook) /* plugin does not support settings */
944a8264
AG
270 {
271 lxpanel_put_line(f, "%s%s {", buf->str, setting->name);
17fab6e5 272 setting->hook(setting, f, setting->hook_data);
944a8264
AG
273 lxpanel_put_line(f, "%s}", buf->str);
274 /* old settings ways are kinda weird... */
275 }
0260eac5
AG
276 else
277 {
944a8264
AG
278 if (out)
279 {
280 g_string_append(out, buf->str);
281 g_string_append(out, setting->name);
282 g_string_append(out, " {\n");
283 }
284 else
285 fprintf(f, "%s%s {\n", buf->str, setting->name);
0260eac5
AG
286 g_string_append(buf, SETTING_INDENT);
287 for (s = setting->first; s; s = s->next)
288 _config_write_setting(s, buf, out, f);
289 g_string_truncate(buf, indent);
290 if (out)
291 {
292 g_string_append(out, buf->str);
293 g_string_append(out, "}\n");
294 }
295 else
296 fprintf(f, "%s}\n", buf->str);
297 }
298 return;
299 case PANEL_CONF_TYPE_LIST:
300 if (setting->name[0] != '\0')
301 {
302 g_warning("only anonymous lists are supported in panel config, got \"%s\"",
303 setting->name);
304 return;
305 }
0260eac5
AG
306 for (s = setting->first; s; s = s->next)
307 _config_write_setting(s, buf, out, f);
0260eac5
AG
308 return;
309 }
310 if (out)
311 g_string_append(out, buf->str);
312 else
313 fputs(buf->str, f);
314 g_string_truncate(buf, indent);
315}
316
317gboolean config_write_file(PanelConf * config, const char * filename)
318{
319 FILE *f = fopen(filename, "w");
320 GString *str;
321 if (f == NULL)
322 return FALSE;
944a8264
AG
323 fputs("# lxpanel <profile> config file. Manually editing is not recommended.\n"
324 "# Use preference dialog in lxpanel to adjust config when you can.\n\n", f);
325 str = g_string_sized_new(128);
326 _config_write_setting(config_setting_get_member(config->root, ""), str, NULL, f);
0260eac5
AG
327 /* FIXME: handle errors */
328 fclose(f);
329 g_string_free(str, TRUE);
330 return TRUE;
331}
332
333/* it is used for old plugins only */
334char * config_setting_to_string(const config_setting_t * setting)
335{
336 GString *val, *buf;
337 g_return_val_if_fail(setting, NULL);
338 val = g_string_sized_new(128);
339 buf = g_string_sized_new(128);
340 _config_write_setting(setting, val, buf, NULL);
341 g_string_free(val, TRUE);
342 return g_string_free(buf, FALSE);
343}
344
345config_setting_t * config_root_setting(const PanelConf * config)
346{
347 return config->root;
348}
349
17fab6e5 350static inline config_setting_t * _config_setting_get_member(const config_setting_t * setting, const char * name)
0260eac5
AG
351{
352 config_setting_t *s;
0260eac5 353 for (s = setting->first; s; s = s->next)
17fab6e5 354 if (g_strcmp0(s->name, name) == 0)
0260eac5
AG
355 break;
356 return s;
357}
358
17fab6e5
AG
359config_setting_t * config_setting_get_member(const config_setting_t * setting, const char * name)
360{
361 g_return_val_if_fail(name && setting, NULL);
362 g_return_val_if_fail(setting->type == PANEL_CONF_TYPE_GROUP, NULL);
363 return _config_setting_get_member(setting, name);
364}
365
0260eac5
AG
366config_setting_t * config_setting_get_elem(const config_setting_t * setting, unsigned int index)
367{
368 config_setting_t *s;
17fab6e5
AG
369 g_return_val_if_fail(setting, NULL);
370 g_return_val_if_fail(setting->type == PANEL_CONF_TYPE_LIST || setting->type == PANEL_CONF_TYPE_GROUP, NULL);
0260eac5
AG
371 for (s = setting->first; s && index > 0; s = s->next)
372 index--;
373 return s;
374}
375
17fab6e5
AG
376const char * config_setting_get_name(const config_setting_t * setting)
377{
378 return setting->name;
379}
380
381config_setting_t * config_setting_get_parent(const config_setting_t * setting)
382{
383 return setting->parent;
384}
385
0260eac5
AG
386int config_setting_get_int(const config_setting_t * setting)
387{
388 if (!setting || setting->type != PANEL_CONF_TYPE_INT)
389 return 0;
390 return setting->num;
391}
392
393const char * config_setting_get_string(const config_setting_t * setting)
394{
395 if (!setting || setting->type != PANEL_CONF_TYPE_STRING)
17fab6e5 396 return NULL;
0260eac5
AG
397 return setting->str;
398}
4bca3e51
AG
399
400gboolean config_setting_lookup_int(const config_setting_t * setting,
401 const char * name, int * value)
402{
403 config_setting_t *sub;
404
405 g_return_val_if_fail(name && setting && value, FALSE);
406 g_return_val_if_fail(setting->type == PANEL_CONF_TYPE_GROUP, FALSE);
407 sub = _config_setting_get_member(setting, name);
408 if (!sub || sub->type != PANEL_CONF_TYPE_INT)
409 return FALSE;
2b363b78 410 *value = sub->num;
4bca3e51
AG
411 return TRUE;
412}
413
414gboolean config_setting_lookup_string(const config_setting_t * setting,
415 const char * name, const char ** value)
416{
417 config_setting_t *sub;
418
419 g_return_val_if_fail(name && setting && value, FALSE);
420 g_return_val_if_fail(setting->type == PANEL_CONF_TYPE_GROUP, FALSE);
421 sub = _config_setting_get_member(setting, name);
422 if (!sub || sub->type != PANEL_CONF_TYPE_STRING)
423 return FALSE;
2b363b78 424 *value = sub->str;
4bca3e51
AG
425 return TRUE;
426}
0260eac5
AG
427
428/* returns either new or existing setting struct, NULL on error */
429config_setting_t * config_setting_add(config_setting_t * parent, const char * name, PanelConfType type)
430{
431 config_setting_t *s;
432 if (parent == NULL || (parent->type != PANEL_CONF_TYPE_GROUP && parent->type != PANEL_CONF_TYPE_LIST))
433 return NULL;
434 if (type == PANEL_CONF_TYPE_LIST)
435 {
436 if (!name || name[0])
437 /* only anonymous lists are supported */
438 return NULL;
439 }
440 else if (name == NULL || name[0] == '\0')
441 /* other types should be not anonymous */
442 return NULL;
17fab6e5
AG
443 if (parent->type == PANEL_CONF_TYPE_GROUP &&
444 (s = _config_setting_get_member(parent, name)))
0260eac5
AG
445 return (s->type == type) ? s : NULL;
446 return _config_setting_t_new(parent, -1, name, type);
447}
448
94f56022
HG
449
450static void remove_from_parent(config_setting_t * setting)
451{
452 config_setting_t *s;
453
454 if (setting->parent->first == setting) {
455 setting->parent->first = setting->next;
456 goto _isolate_setting;
457 }
458
459 for (s = setting->parent->first; s->next; s = s->next)
460 if (s->next == setting)
461 break;
462 g_assert(s->next);
463 s->next = setting->next;
464
465_isolate_setting:
466 setting->next = NULL;
467 setting->parent = NULL;
468}
469
470static void append_to_parent(config_setting_t * setting, config_setting_t * parent)
0260eac5
AG
471{
472 config_setting_t *s;
473
94f56022
HG
474 setting->parent = parent;
475 if (parent->first == NULL) {
476 parent->first = setting;
477 return;
478 }
479
480 s = parent->first;
481 while (s->next)
482 s = s->next;
483 s->next = setting;
484}
485
486static void insert_after(config_setting_t * setting, config_setting_t * parent,
487 config_setting_t * prev)
488{
489 setting->parent = parent;
490 if (prev == NULL) {
491 setting->next = parent->first;
492 parent->first = setting;
493 } else {
494 setting->next = prev->next;
495 prev->next = setting;
496 }
497}
498
499gboolean config_setting_move_member(config_setting_t * setting, config_setting_t * parent, const char * name)
500{
537ec037 501 config_setting_t *s;
94f56022 502
0260eac5
AG
503 g_return_val_if_fail(setting && setting->parent, FALSE);
504 if (parent == NULL || name == NULL || parent->type != PANEL_CONF_TYPE_GROUP)
505 return FALSE;
17fab6e5 506 s = _config_setting_get_member(parent, name);
0260eac5
AG
507 if (s) /* we cannot rename/move to this name, it exists already */
508 return (s == setting);
509 if (setting->parent == parent) /* it's just renaming thing */
510 goto _rename;
94f56022
HG
511 remove_from_parent(setting); /* remove from old parent */
512 append_to_parent(setting, parent); /* add to new parent */
0260eac5
AG
513 /* rename if need */
514 if (g_strcmp0(setting->name, name) != 0)
515 {
516_rename:
517 g_free(setting->name);
518 setting->name = g_strdup(name);
519 }
520 return TRUE;
521}
522
523gboolean config_setting_move_elem(config_setting_t * setting, config_setting_t * parent, int index)
524{
537ec037 525 config_setting_t *prev = NULL;
0260eac5
AG
526
527 g_return_val_if_fail(setting && setting->parent, FALSE);
528 if (parent == NULL || parent->type != PANEL_CONF_TYPE_LIST)
529 return FALSE;
530 if (setting->type != PANEL_CONF_TYPE_GROUP) /* we support only list of groups now */
531 return FALSE;
532 /* let check the place */
533 if (index != 0)
534 {
94f56022
HG
535 prev = parent->first;
536 if (prev)
0260eac5
AG
537 for ( ; index != 1 && prev->next; prev = prev->next)
538 index--;
539 if (index > 1) /* too few elements yet */
540 {
3d6ee560 541_out_of_range:
0260eac5
AG
542 g_warning("config_setting_move_elem: index out of range");
543 return FALSE;
544 }
545 if (prev && prev->next == setting) /* it is already there */
546 return TRUE;
761af06c
AG
547 if (prev == setting) /* special case: we moving it +1, swapping with next */
548 {
549 if (prev->next == NULL)
550 goto _out_of_range;
551 prev = prev->next;
552 }
0260eac5
AG
553 }
554 else if (parent->first == setting) /* it is already there */
555 return TRUE;
94f56022 556 remove_from_parent(setting); /* remove from old parent */
0260eac5 557 /* add to new parent */
0260eac5 558 if (index == 0)
94f56022
HG
559 g_assert(prev == NULL);
560 insert_after(setting, parent, prev);
0260eac5
AG
561 /* don't rename */
562 return TRUE;
563}
564
565gboolean config_setting_set_int(config_setting_t * setting, int value)
566{
567 if (!setting || setting->type != PANEL_CONF_TYPE_INT)
568 return FALSE;
569 setting->num = value;
570 return TRUE;
571}
572
573gboolean config_setting_set_string(config_setting_t * setting, const char * value)
574{
575 if (!setting || setting->type != PANEL_CONF_TYPE_STRING)
576 return FALSE;
577 g_free(setting->str);
578 setting->str = g_strdup(value);
579 return TRUE;
580}
581
582gboolean config_setting_remove(config_setting_t * parent, const char * name)
583{
584 config_setting_t *s = config_setting_get_member(parent, name);
585 if (s == NULL)
586 return FALSE;
587 _config_setting_t_remove(s);
588 return TRUE;
589}
590
591gboolean config_setting_remove_elem(config_setting_t * parent, unsigned int index)
592{
593 config_setting_t *s = config_setting_get_elem(parent, index);
594 if (s == NULL)
595 return FALSE;
596 _config_setting_t_remove(s);
597 return TRUE;
598}
599
17fab6e5
AG
600gboolean config_setting_destroy(config_setting_t * setting)
601{
602 if (setting == NULL || setting->parent == NULL)
603 return FALSE;
604 _config_setting_t_remove(setting);
605 return TRUE;
606}
607
0260eac5
AG
608PanelConfType config_setting_type(const config_setting_t * setting)
609{
610 return setting->type;
611}
612
17fab6e5
AG
613void config_setting_set_save_hook(config_setting_t * setting, PanelConfSaveHook hook,
614 gpointer user_data)
0260eac5
AG
615{
616 setting->hook = hook;
17fab6e5 617 setting->hook_data = user_data;
0260eac5 618}