Remove unused variables.
[lxde/lxpanel.git] / src / conf.c
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"
24 #include "private.h"
25
26 #include <string.h>
27 #include <stdlib.h>
28
29 struct _config_setting_t
30 {
31 config_setting_t *next;
32 config_setting_t *parent;
33 PanelConfType type;
34 PanelConfSaveHook hook;
35 gpointer hook_data;
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
44 struct _PanelConf
45 {
46 config_setting_t *root;
47 };
48
49 static 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 */
76 static 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 */
100 static 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
119 PanelConf *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);
123 return c;
124 }
125
126 void config_destroy(PanelConf * config)
127 {
128 _config_setting_t_free(config->root);
129 g_slice_free(PanelConf, config);
130 }
131
132 gboolean config_read_file(PanelConf * config, const char * filename)
133 {
134 FILE *f = fopen(filename, "r");
135 size_t size;
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);
145 size = fread(buff, 1, size, f);
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 {
176 g_warning("config: invalid scalar definition");
177 goto _skip_all;
178 }
179 while (*c == ' ' || *c == '\t')
180 c++; /* skip spaces after '=' */
181 if (name == NULL || *c == '\0' || *c == '\n') /* invalid statement */
182 break;
183 size = strtol(c, &end, 10);
184 while (*end == ' ' || *end == '\t')
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)
190 {
191 s->num = (int)size;
192 /* g_debug("config loader: got new int %s: %d", name, s->num); */
193 }
194 else
195 g_warning("config: duplicate setting '%s' conflicts, ignored", name);
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);
206 /* g_debug("config loader: got new string %s: %s", name, s->str); */
207 }
208 else
209 g_warning("config: duplicate setting '%s' conflicts, ignored", name);
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;
226 /* g_debug("config loader: group '%s' added", name); */
227 }
228 else
229 g_warning("config: invalid group '%s' in config file ignored", name);
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
252 static 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:
264 if (!setting->str) /* don't save NULL strings */
265 return;
266 g_string_append_printf(buf, "%s=%s\n", setting->name, setting->str);
267 break;
268 case PANEL_CONF_TYPE_GROUP:
269 if (!out && setting->hook) /* plugin does not support settings */
270 {
271 lxpanel_put_line(f, "%s%s {", buf->str, setting->name);
272 setting->hook(setting, f, setting->hook_data);
273 lxpanel_put_line(f, "%s}", buf->str);
274 /* old settings ways are kinda weird... */
275 }
276 else
277 {
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);
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 }
306 for (s = setting->first; s; s = s->next)
307 _config_write_setting(s, buf, out, f);
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
317 gboolean 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;
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);
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 */
334 char * 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
345 config_setting_t * config_root_setting(const PanelConf * config)
346 {
347 return config->root;
348 }
349
350 static inline config_setting_t * _config_setting_get_member(const config_setting_t * setting, const char * name)
351 {
352 config_setting_t *s;
353 for (s = setting->first; s; s = s->next)
354 if (g_strcmp0(s->name, name) == 0)
355 break;
356 return s;
357 }
358
359 config_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
366 config_setting_t * config_setting_get_elem(const config_setting_t * setting, unsigned int index)
367 {
368 config_setting_t *s;
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);
371 for (s = setting->first; s && index > 0; s = s->next)
372 index--;
373 return s;
374 }
375
376 const char * config_setting_get_name(const config_setting_t * setting)
377 {
378 return setting->name;
379 }
380
381 config_setting_t * config_setting_get_parent(const config_setting_t * setting)
382 {
383 return setting->parent;
384 }
385
386 int 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
393 const char * config_setting_get_string(const config_setting_t * setting)
394 {
395 if (!setting || setting->type != PANEL_CONF_TYPE_STRING)
396 return NULL;
397 return setting->str;
398 }
399
400 gboolean 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;
410 *value = sub->num;
411 return TRUE;
412 }
413
414 gboolean 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;
424 *value = sub->str;
425 return TRUE;
426 }
427
428 /* returns either new or existing setting struct, NULL on error */
429 config_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;
443 if (parent->type == PANEL_CONF_TYPE_GROUP &&
444 (s = _config_setting_get_member(parent, name)))
445 return (s->type == type) ? s : NULL;
446 return _config_setting_t_new(parent, -1, name, type);
447 }
448
449
450 static 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
470 static void append_to_parent(config_setting_t * setting, config_setting_t * parent)
471 {
472 config_setting_t *s;
473
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
486 static 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
499 gboolean config_setting_move_member(config_setting_t * setting, config_setting_t * parent, const char * name)
500 {
501 config_setting_t *s;
502
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;
506 s = _config_setting_get_member(parent, name);
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;
511 remove_from_parent(setting); /* remove from old parent */
512 append_to_parent(setting, parent); /* add to new parent */
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
523 gboolean config_setting_move_elem(config_setting_t * setting, config_setting_t * parent, int index)
524 {
525 config_setting_t *prev = NULL;
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 {
535 prev = parent->first;
536 if (prev)
537 for ( ; index != 1 && prev->next; prev = prev->next)
538 index--;
539 if (index > 1) /* too few elements yet */
540 {
541 _out_of_range:
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;
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 }
553 }
554 else if (parent->first == setting) /* it is already there */
555 return TRUE;
556 remove_from_parent(setting); /* remove from old parent */
557 /* add to new parent */
558 if (index == 0)
559 g_assert(prev == NULL);
560 insert_after(setting, parent, prev);
561 /* don't rename */
562 return TRUE;
563 }
564
565 gboolean 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
573 gboolean 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
582 gboolean 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
591 gboolean 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
600 gboolean 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
608 PanelConfType config_setting_type(const config_setting_t * setting)
609 {
610 return setting->type;
611 }
612
613 void config_setting_set_save_hook(config_setting_t * setting, PanelConfSaveHook hook,
614 gpointer user_data)
615 {
616 setting->hook = hook;
617 setting->hook_data = user_data;
618 }