1 /* $Id$ */
2 /*-
3 * Copyright (c) 2003-2007 Benedikt Meurer <benny@xfce.org>
4 * All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #ifdef HAVE_ERRNO_H
27 #include <errno.h>
28 #endif
29 #ifdef HAVE_MEMORY_H
30 #include <memory.h>
31 #endif
32 #include <stdio.h>
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #endif
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include <libxfce4util/xfce-private.h>
41 #include <libxfce4util/xfce-rc-private.h>
42 #include <libxfce4util/libxfce4util-alias.h>
43
44
45 #ifndef PATH_MAX
46 #define PATH_MAX 4096
47 #endif
48
49 #ifdef LINE_MAX
50 #undef LINE_MAX
51 #endif
52 #define LINE_MAX 8192
53
54 /* name of the NULL group */
55 #define NULL_GROUP "[NULL]"
56
57
58 typedef struct _Entry Entry;
59 typedef struct _LEntry LEntry;
60 typedef struct _Group Group;
61
62
63
64 static Group* simple_add_group (XfceRcSimple *simple,
65 const gchar *name);
66 static Entry* simple_add_entry (XfceRcSimple *simple,
67 const gchar *key,
68 const gchar *value,
69 const gchar *locale);
70 static gboolean simple_parse_line (gchar *line,
71 gchar **section,
72 gchar **key,
73 gchar **value,
74 gchar **locale);
75 static gchar* simple_escape (gchar *buffer,
76 gssize size,
77 const gchar *string);
78 static gboolean simple_write (XfceRcSimple *simple,
79 const gchar *filename);
80 static void simple_entry_free (Entry *entry);
81 static void simple_group_free (Group *group);
82
83
84
85 struct _XfceRcSimple
86 {
87 XfceRc __parent__;
88
89 GStringChunk *string_chunk;
90
91 gchar *filename;
92
93 Group *gfirst;
94 Group *glast;
95 Group *group;
96
97 guint shared_chunks : 1;
98 guint dirty : 1;
99 guint readonly : 1;
100 };
101
102 struct _Entry
103 {
104 gchar *key;
105 gchar *value;
106 Entry *next;
107 Entry *prev;
108 LEntry *lfirst;
109 LEntry *llast;
110 };
111
112 struct _LEntry
113 {
114 gchar *locale;
115 gchar *value;
116 LEntry *next;
117 LEntry *prev;
118 };
119
120 struct _Group
121 {
122 gchar *name;
123 Group *next;
124 Group *prev;
125 Entry *efirst;
126 Entry *elast;
127 };
128
129
130
131 /* because strcmp is by far the most called function in this code,
132 * we inline the comparison of the first character */
133 #define str_is_equal(a,b) (*(a) != *(b) ? FALSE : strcmp ((a), (b)) == 0)
134
135
136
137 static Group*
simple_add_group(XfceRcSimple * simple,const gchar * name)138 simple_add_group (XfceRcSimple *simple,
139 const gchar *name)
140 {
141 Group *group;
142
143 for (group = simple->gfirst; group != NULL; group = group->next)
144 if (str_is_equal (group->name, name))
145 return group;
146
147 group = g_slice_new (Group);
148 group->name = g_string_chunk_insert (simple->string_chunk, name);
149 group->efirst = NULL;
150 group->elast = NULL;
151
152 if (G_UNLIKELY (simple->gfirst == NULL))
153 {
154 group->next = group->prev = NULL;
155 simple->gfirst = simple->glast = group;
156 }
157 else
158 {
159 group->next = NULL;
160 group->prev = simple->glast;
161 simple->glast->next = group;
162 simple->glast = group;
163 }
164
165 return group;
166 }
167
168
169
170 static Entry*
simple_add_entry(XfceRcSimple * simple,const gchar * key,const gchar * value,const gchar * locale)171 simple_add_entry (XfceRcSimple *simple,
172 const gchar *key,
173 const gchar *value,
174 const gchar *locale)
175 {
176 LEntry *lentry_before;
177 LEntry *lentry;
178 Entry *entry;
179 gint result;
180
181 for (entry = simple->group->efirst; entry != NULL; entry = entry->next)
182 if (str_is_equal (entry->key, key))
183 break;
184
185 if (G_UNLIKELY (entry == NULL))
186 {
187 entry = g_slice_new (Entry);
188 entry->key = g_string_chunk_insert (simple->string_chunk, key);
189 entry->value = g_string_chunk_insert (simple->string_chunk, value);
190 entry->lfirst = NULL;
191 entry->llast = NULL;
192
193 if (simple->group->efirst == NULL)
194 {
195 entry->next = entry->prev = NULL;
196 simple->group->efirst = simple->group->elast = entry;
197 }
198 else
199 {
200 entry->next = NULL;
201 entry->prev = simple->group->elast;
202 simple->group->elast->next = entry;
203 simple->group->elast = entry;
204 }
205
206 if (locale == NULL)
207 return entry;
208 }
209
210 if (G_UNLIKELY (locale == NULL))
211 {
212 /* overwrite existing value */
213 if (!str_is_equal (entry->value, value))
214 entry->value = g_string_chunk_insert (simple->string_chunk, value);
215 }
216 else
217 {
218 /* Add the localized value. Note the optimization used within here: We store
219 * the locales in alphabetic order (e.g. de, fr, ...). And we start the search
220 * at the end of the locales list, since locales are usually sorted in the
221 * rc files. Once we notice that the locale in question is less than the
222 * current list locale, we cancel the search and remember the list element as
223 * the element before the new list element (remember the sorting order). This
224 * makes parsing a usual resource config file with lots of locales amazingly
225 * fast.
226 */
227 lentry_before = NULL;
228 for (lentry = entry->llast; lentry != NULL; lentry = lentry->prev)
229 {
230 result = strcmp (lentry->locale, locale);
231
232 if (result == 0)
233 break;
234 else if (result < 0)
235 {
236 lentry_before = lentry;
237 lentry = NULL;
238 break;
239 }
240 }
241
242 if (G_LIKELY (lentry == NULL))
243 {
244 /* create new localized entry */
245 lentry = g_slice_new (LEntry);
246 lentry->locale = g_string_chunk_insert (simple->string_chunk, locale);
247 lentry->value = g_string_chunk_insert (simple->string_chunk, value);
248
249 if (G_UNLIKELY (entry->lfirst == NULL))
250 {
251 lentry->next = lentry->prev = NULL;
252 entry->lfirst = entry->llast = lentry;
253 }
254 else if (lentry_before != NULL)
255 {
256 lentry->next = lentry_before->next;
257 lentry->prev = lentry_before;
258 if (G_UNLIKELY (lentry_before->next != NULL))
259 lentry_before->next->prev = lentry;
260 else
261 entry->llast = lentry;
262 lentry_before->next = lentry;
263 }
264 else
265 {
266 lentry->next = NULL;
267 lentry->prev = entry->llast;
268 entry->llast->next = lentry;
269 entry->llast = lentry;
270 }
271 }
272 else
273 {
274 /* overwrite value in existing localized entry */
275 if (G_LIKELY (!str_is_equal (lentry->value, value)))
276 lentry->value = g_string_chunk_insert (simple->string_chunk, value);
277 }
278 }
279
280 return entry;
281 }
282
283
284
285 static gboolean
simple_parse_line(gchar * line,gchar ** section,gchar ** key,gchar ** value,gchar ** locale)286 simple_parse_line (gchar *line,
287 gchar **section,
288 gchar **key,
289 gchar **value,
290 gchar **locale)
291 {
292 gchar *p, *q, *r, *s;
293
294 p = line;
295
296 *section = NULL;
297 *locale = NULL;
298 *value = NULL;
299 *key = NULL;
300
301 while (g_ascii_isspace (*p))
302 ++p;
303
304 if (G_UNLIKELY (*p == '\0' || *p == '\n' || *p == '#'))
305 return FALSE;
306
307 if (*p == '[')
308 {
309 q = strrchr (++p, ']');
310 if (G_LIKELY (q != NULL))
311 {
312 *section = p;
313 *q = '\0';
314 return TRUE;
315 }
316 }
317 else
318 {
319 q = strchr (p + 1, '=');
320 if (G_UNLIKELY (q == NULL))
321 return FALSE;
322
323 r = q + 1;
324
325 for (--q; g_ascii_isspace (*q); --q)
326 ;
327
328 if (G_UNLIKELY (q == p))
329 return FALSE;
330
331 if (*q == ']')
332 {
333 for (s = q - 1; *s != '[' && s > p; --s)
334 ;
335 if (G_UNLIKELY (*s != '['))
336 return FALSE;
337
338 *key = p;
339 *s = '\0';
340
341 *locale = s + 1;
342 *q = '\0';
343
344 if (G_UNLIKELY (**key == '\0' || **locale == '\0'))
345 return FALSE;
346 }
347 else
348 {
349 ++q;
350 *key = p;
351 *q = '\0';
352 }
353
354 while (g_ascii_isspace (*r))
355 ++r;
356
357 q = r + strlen (r);
358
359 /* "\ " at the end of the string will not be removed */
360 while (q > r && ((g_ascii_isspace (*(q-1)) && *(q-2) != '\\') || ((*(q-1)) == '\r')))
361 --q;
362
363 *value = r;
364 *q = '\0';
365
366 /* unescape \ , \n, \t, \r and \\ */
367 for (p = r; *r != '\0'; )
368 {
369 if (G_UNLIKELY (*r == '\\'))
370 {
371 switch (*(r+1))
372 {
373 case ' ':
374 *p++ = ' ';
375 break;
376
377 case 'n':
378 *p++ = '\n';
379 break;
380
381 case 't':
382 *p++ = '\t';
383 break;
384
385 case 'r':
386 *p++ = '\r';
387 break;
388
389 case '\\':
390 *p++ = '\\';
391 break;
392
393 default:
394 *p++ = '\\';
395 *p++ = *(r+1);
396 break;
397 }
398
399 r += 2;
400 }
401 else
402 *p++ = *r++;
403 }
404 *p = '\0';
405
406 return TRUE;
407 }
408
409 return FALSE;
410 }
411
412
413
414 static gchar*
simple_escape(gchar * buffer,gssize size,const gchar * string)415 simple_escape (gchar *buffer, gssize size, const gchar *string)
416 {
417 const gchar *s;
418 gchar *p;
419
420 /* escape all whitespace at the beginning of the string */
421 for (p = buffer; p - buffer < size - 2 && *string == ' '; ++string)
422 {
423 *p++ = '\\';
424 *p++ = ' ';
425 }
426
427 for (; p - buffer < size - 2 && *string != '\0'; ++string)
428 switch (*string)
429 {
430 case ' ':
431 /* check if any non whitespace characters follow */
432 for (s = string + 1; g_ascii_isspace (*s); ++s)
433 ;
434 if (*s == '\0')
435 {
436 /* need to escape the space */
437 *p++ = '\\';
438 *p++ = ' ';
439 }
440 else
441 {
442 /* still non-whitespace, no need to escape */
443 *p++ = ' ';
444 }
445 break;
446
447 case '\n':
448 *p++ = '\\';
449 *p++ = 'n';
450 break;
451
452 case '\t':
453 *p++ = '\\';
454 *p++ = 't';
455 break;
456
457 case '\r':
458 *p++ = '\\';
459 *p++ = 'r';
460 break;
461
462 case '\\':
463 *p++ = '\\';
464 *p++ = '\\';
465 break;
466
467 default:
468 *p++ = *string;
469 break;
470 }
471
472 *p = '\0';
473 return buffer;
474 }
475
476
477
478 static gboolean
simple_write(XfceRcSimple * simple,const gchar * filename)479 simple_write (XfceRcSimple *simple, const gchar *filename)
480 {
481 LEntry *lentry;
482 Entry *entry;
483 Group *group;
484 gchar buffer[LINE_MAX];
485 FILE *fp;
486
487 fp = fopen (filename, "w");
488 if (G_UNLIKELY (fp == NULL))
489 {
490 g_critical ("Unable to open file %s for writing: %s", filename, g_strerror (errno));
491 return FALSE;
492 }
493
494 for (group = simple->gfirst; group != NULL; group = group->next)
495 {
496 /* don't store empty groups */
497 if (group->efirst == NULL)
498 continue;
499
500 /* NULL_GROUP has no header */
501 if (!str_is_equal (group->name, NULL_GROUP))
502 fprintf (fp, "[%s]\n", group->name);
503
504 for (entry = group->efirst; entry != NULL; entry = entry->next)
505 {
506 simple_escape (buffer, LINE_MAX, entry->value);
507 fprintf (fp, "%s=%s\n", entry->key, buffer);
508
509 for (lentry = entry->lfirst; lentry != NULL; lentry = lentry->next)
510 {
511 simple_escape (buffer, LINE_MAX, lentry->value);
512 fprintf (fp, "%s[%s]=%s\n", entry->key, lentry->locale, buffer);
513 }
514 }
515
516 fprintf (fp, "\n");
517 }
518
519 if (ferror (fp))
520 {
521 g_critical ("Unable to write to file %s: Unexpected internal error", filename);
522 fclose (fp);
523 unlink (filename);
524 return FALSE;
525 }
526
527 fclose (fp);
528 return TRUE;
529 }
530
531
532
533 static void
simple_entry_free(Entry * entry)534 simple_entry_free (Entry *entry)
535 {
536 /* release all lentries */
537 g_slice_free_chain (LEntry, entry->lfirst, next);
538
539 /* release the entry */
540 g_slice_free (Entry, entry);
541 }
542
543
544
545 static void
simple_group_free(Group * group)546 simple_group_free (Group *group)
547 {
548 Entry *entry;
549 Entry *next;
550
551 /* release all entries */
552 for (entry = group->efirst; entry != NULL; entry = next)
553 {
554 /* determine the next entry */
555 next = entry->next;
556
557 /* release this entry */
558 simple_entry_free (entry);
559 }
560
561 /* release the group */
562 g_slice_free (Group, group);
563 }
564
565
566
567 XfceRcSimple*
_xfce_rc_simple_new(XfceRcSimple * shared,const gchar * filename,gboolean readonly)568 _xfce_rc_simple_new (XfceRcSimple *shared,
569 const gchar *filename,
570 gboolean readonly)
571 {
572 XfceRcSimple *simple;
573
574 simple = g_new0 (XfceRcSimple, 1);
575
576 _xfce_rc_init (XFCE_RC (simple));
577
578 /* attach callbacks */
579 simple->__parent__.close = _xfce_rc_simple_close;
580 simple->__parent__.get_groups = _xfce_rc_simple_get_groups;
581 simple->__parent__.get_entries = _xfce_rc_simple_get_entries;
582 simple->__parent__.delete_group = _xfce_rc_simple_delete_group;
583 simple->__parent__.get_group = _xfce_rc_simple_get_group;
584 simple->__parent__.has_group = _xfce_rc_simple_has_group;
585 simple->__parent__.set_group = _xfce_rc_simple_set_group;
586 simple->__parent__.delete_entry = _xfce_rc_simple_delete_entry;
587 simple->__parent__.has_entry = _xfce_rc_simple_has_entry;
588 simple->__parent__.read_entry = _xfce_rc_simple_read_entry;
589
590 if (!readonly)
591 {
592 simple->__parent__.flush = _xfce_rc_simple_flush;
593 simple->__parent__.rollback = _xfce_rc_simple_rollback;
594 simple->__parent__.is_dirty = _xfce_rc_simple_is_dirty;
595 simple->__parent__.is_readonly = _xfce_rc_simple_is_readonly;
596 simple->__parent__.write_entry = _xfce_rc_simple_write_entry;
597 }
598
599 if (shared != NULL)
600 {
601 simple->shared_chunks = TRUE;
602 simple->string_chunk = shared->string_chunk;
603 }
604 else
605 {
606 simple->shared_chunks = FALSE;
607 simple->string_chunk = g_string_chunk_new (4096);
608 }
609
610 simple->filename = g_string_chunk_insert (simple->string_chunk, filename);
611 simple->readonly = readonly;
612
613 /* add NULL_GROUP */
614 simple->group = simple_add_group (simple, NULL_GROUP);
615
616 return simple;
617 }
618
619
620
621 gboolean
_xfce_rc_simple_parse(XfceRcSimple * simple)622 _xfce_rc_simple_parse (XfceRcSimple *simple)
623 {
624 gboolean readonly;
625 gchar line[LINE_MAX];
626 gchar *section;
627 gchar *locale;
628 gchar *value;
629 gchar *key;
630 XfceRc *rc;
631 FILE *fp;
632
633 _xfce_return_val_if_fail (simple != NULL, FALSE);
634 _xfce_return_val_if_fail (simple->filename != NULL, FALSE);
635
636 rc = XFCE_RC (simple);
637 readonly = xfce_rc_is_readonly (rc);
638
639 fp = fopen (simple->filename, "r");
640 if (fp == NULL)
641 return FALSE;
642
643 while (fgets (line, LINE_MAX, fp) != NULL)
644 {
645 if (!simple_parse_line (line, §ion, &key, &value, &locale))
646 continue;
647
648 if (section != NULL)
649 {
650 simple->group = simple_add_group (simple, section);
651 continue;
652 }
653
654 if (locale == NULL)
655 {
656 simple_add_entry (simple, key, value, NULL);
657 continue;
658 }
659
660 if (rc->locale == NULL)
661 continue;
662
663 if (!readonly || xfce_locale_match (rc->locale, locale) > XFCE_LOCALE_NO_MATCH)
664 simple_add_entry (simple, key, value, locale);
665 }
666
667 fclose (fp);
668
669 return TRUE;
670 }
671
672
673
674 void
_xfce_rc_simple_close(XfceRc * rc)675 _xfce_rc_simple_close (XfceRc *rc)
676 {
677 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
678 Group *group_next;
679 Group *group;
680
681 /* release all memory allocated to the groups */
682 for (group = simple->gfirst; group != NULL; group = group_next)
683 {
684 /* determine the next group */
685 group_next = group->next;
686
687 /* release this group */
688 simple_group_free (group);
689 }
690
691 /* release the string chunk */
692 if (!simple->shared_chunks)
693 g_string_chunk_free (simple->string_chunk);
694 }
695
696
697
698 void
_xfce_rc_simple_flush(XfceRc * rc)699 _xfce_rc_simple_flush (XfceRc *rc)
700 {
701 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
702 gchar *filename = simple->filename;
703 gchar tmp_path[PATH_MAX], buf[PATH_MAX] = {0};
704
705 if (G_UNLIKELY (!simple->dirty))
706 return;
707
708 g_snprintf (tmp_path, PATH_MAX, "%s.%d.tmp", simple->filename, (int)getpid ());
709 if (simple_write (simple, tmp_path))
710 {
711 /* support rc file being a symlink: see bug #14698 */
712 if (readlink (simple->filename, buf, sizeof (buf)-1) != -1)
713 filename = buf;
714
715 if (rename (tmp_path, filename) < 0)
716 {
717 g_critical ("Unable to rename %s to %s: %s",
718 tmp_path,
719 filename,
720 g_strerror (errno));
721 unlink (tmp_path);
722 }
723 else
724 simple->dirty = FALSE;
725 }
726 }
727
728
729
730 void
_xfce_rc_simple_rollback(XfceRc * rc)731 _xfce_rc_simple_rollback (XfceRc *rc)
732 {
733 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
734
735 simple->dirty = FALSE;
736 }
737
738
739
740 gboolean
_xfce_rc_simple_is_dirty(const XfceRc * rc)741 _xfce_rc_simple_is_dirty (const XfceRc *rc)
742 {
743 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
744
745 return simple->dirty;
746 }
747
748
749
750 gboolean
_xfce_rc_simple_is_readonly(const XfceRc * rc)751 _xfce_rc_simple_is_readonly (const XfceRc *rc)
752 {
753 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
754
755 return simple->readonly;
756 }
757
758
759
760 const gchar*
_xfce_rc_simple_get_filename(const XfceRc * rc)761 _xfce_rc_simple_get_filename (const XfceRc *rc)
762 {
763 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
764
765 return simple->filename;
766 }
767
768
769
770 gchar**
_xfce_rc_simple_get_groups(const XfceRc * rc)771 _xfce_rc_simple_get_groups (const XfceRc *rc)
772 {
773 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
774 const Group *group;
775 gchar **result;
776 guint size;
777 guint pos;
778
779 result = g_new (gchar *, 11);
780 size = 10;
781 pos = 0;
782
783 for (group = simple->gfirst; group != NULL; group = group->next)
784 {
785 if (pos == size)
786 {
787 size *= 2;
788 result = g_realloc (result, (size + 1) * sizeof (*result));
789 }
790 result[pos] = g_strdup (group->name);
791 ++pos;
792 }
793
794 result[pos] = NULL;
795 return result;
796 }
797
798
799
800 gchar**
_xfce_rc_simple_get_entries(const XfceRc * rc,const gchar * name)801 _xfce_rc_simple_get_entries (const XfceRc *rc,
802 const gchar *name)
803 {
804 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
805 const Group *group;
806 const Entry *entry;
807 gchar **result;
808 guint size;
809 guint pos;
810
811 if (name == NULL)
812 name = NULL_GROUP;
813
814 for (group = simple->gfirst; group != NULL; group = group->next)
815 if (str_is_equal (group->name, name))
816 break;
817
818 if (group == NULL)
819 return NULL;
820
821 result = g_new (gchar *, 11);
822 size = 10;
823 pos = 0;
824
825 for (entry = group->efirst; entry != NULL; entry = entry->next)
826 {
827 if (size == pos)
828 {
829 size *= 2;
830 result = g_realloc (result, (size + 1) * sizeof (*result));
831 }
832 result[pos] = g_strdup (entry->key);
833 ++pos;
834 }
835
836 result[pos] = NULL;
837
838 return result;
839 }
840
841
842
843 void
_xfce_rc_simple_delete_group(XfceRc * rc,const gchar * name,gboolean global)844 _xfce_rc_simple_delete_group (XfceRc *rc,
845 const gchar *name,
846 gboolean global)
847 {
848 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
849 Group *group;
850 Entry *entry;
851 Entry *next;
852
853 if (name == NULL)
854 name = NULL_GROUP;
855
856 for (group = simple->gfirst; group != NULL; group = group->next)
857 {
858 if (str_is_equal (group->name, name))
859 {
860 if (simple->group == group || str_is_equal (name, NULL_GROUP))
861 {
862 /* don't delete current group or the default group, just clear them */
863 for (entry = group->efirst; entry != NULL; entry = next)
864 {
865 next = entry->next;
866 simple_entry_free (entry);
867 }
868 group->efirst = group->elast = NULL;
869 }
870 else
871 {
872 /* unlink group from group list */
873 if (group->prev != NULL)
874 group->prev->next = group->next;
875 else
876 simple->gfirst = group->next;
877 if (group->next != NULL)
878 group->next->prev = group->prev;
879 else
880 simple->glast = group->prev;
881
882 /* delete this group */
883 simple_group_free (group);
884 }
885
886 simple->dirty = TRUE;
887 break;
888 }
889 }
890 }
891
892
893
894 const gchar*
_xfce_rc_simple_get_group(const XfceRc * rc)895 _xfce_rc_simple_get_group (const XfceRc *rc)
896 {
897 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
898
899 if (str_is_equal (simple->group->name, NULL_GROUP))
900 return NULL;
901 else
902 return simple->group->name;
903 }
904
905
906
907 gboolean
_xfce_rc_simple_has_group(const XfceRc * rc,const gchar * name)908 _xfce_rc_simple_has_group (const XfceRc *rc,
909 const gchar *name)
910 {
911 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
912 const Group *group;
913
914 /* the NULL group always exists */
915 if (name == NULL)
916 return TRUE;
917
918 for (group = simple->gfirst; group != NULL; group = group->next)
919 if (str_is_equal (group->name, name))
920 break;
921
922 return group != NULL;
923 }
924
925
926
927 void
_xfce_rc_simple_set_group(XfceRc * rc,const gchar * name)928 _xfce_rc_simple_set_group (XfceRc *rc,
929 const gchar *name)
930 {
931 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
932
933 if (name == NULL)
934 name = NULL_GROUP;
935
936 if (!str_is_equal (simple->group->name, name))
937 simple->group = simple_add_group (simple, name);
938 }
939
940
941
942 void
_xfce_rc_simple_delete_entry(XfceRc * rc,const gchar * key,gboolean global)943 _xfce_rc_simple_delete_entry (XfceRc *rc,
944 const gchar *key,
945 gboolean global)
946 {
947 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
948 Entry *entry;
949
950 for (entry = simple->group->efirst; entry != NULL; entry = entry->next)
951 {
952 if (str_is_equal (entry->key, key))
953 {
954 if (entry->prev != NULL)
955 entry->prev->next = entry->next;
956 else
957 simple->group->efirst = entry->next;
958
959 if (entry->next != NULL)
960 entry->next->prev = entry->prev;
961 else
962 simple->group->elast = entry->prev;
963
964 /* delete this entry */
965 simple_entry_free (entry);
966
967 simple->dirty = TRUE;
968 break;
969 }
970 }
971 }
972
973
974
975 gboolean
_xfce_rc_simple_has_entry(const XfceRc * rc,const gchar * key)976 _xfce_rc_simple_has_entry (const XfceRc *rc,
977 const gchar *key)
978 {
979 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
980 const Entry *entry;
981
982 for (entry = simple->group->efirst; entry != NULL; entry = entry->next)
983 if (str_is_equal (entry->key, key))
984 break;
985
986 return entry != NULL;
987 }
988
989
990
991 const gchar*
_xfce_rc_simple_read_entry(const XfceRc * rc,const gchar * key,gboolean translated)992 _xfce_rc_simple_read_entry (const XfceRc *rc,
993 const gchar *key,
994 gboolean translated)
995 {
996 const XfceRcSimple *simple = XFCE_RC_SIMPLE_CONST (rc);
997 LEntry *lentry;
998 Entry *entry;
999 const gchar *best_value;
1000 guint best_match;
1001 guint match;
1002
1003 for (entry = simple->group->efirst; entry != NULL; entry = entry->next)
1004 if (str_is_equal (entry->key, key))
1005 break;
1006
1007 if (G_LIKELY (entry != NULL))
1008 {
1009 /* check for localized entry (best fit!) */
1010 if (G_LIKELY (translated && rc->locale != NULL))
1011 {
1012 best_match = XFCE_LOCALE_NO_MATCH;
1013 best_value = NULL;
1014
1015 for (lentry = entry->lfirst; lentry != NULL; lentry = lentry->next)
1016 {
1017 match = xfce_locale_match (rc->locale, lentry->locale);
1018 if (match == XFCE_LOCALE_FULL_MATCH)
1019 {
1020 /* FULL MATCH */
1021 return lentry->value;
1022 }
1023 else if (match > best_match)
1024 {
1025 best_match = match;
1026 best_value = lentry->value;
1027 }
1028 }
1029
1030 if (best_value != NULL)
1031 return best_value;
1032
1033 /* FALL-THROUGH */
1034 }
1035
1036 return entry->value;
1037 }
1038
1039 return NULL;
1040 }
1041
1042
1043
1044 void
_xfce_rc_simple_write_entry(XfceRc * rc,const gchar * key,const gchar * value)1045 _xfce_rc_simple_write_entry (XfceRc *rc,
1046 const gchar *key,
1047 const gchar *value)
1048 {
1049 XfceRcSimple *simple = XFCE_RC_SIMPLE (rc);
1050 Entry *result;
1051
1052 result = simple_add_entry (simple, key, value, NULL);
1053 if (G_LIKELY (result != NULL))
1054 simple->dirty = TRUE;
1055 }
1056
1057
1058
1059 #define __XFCE_RC_SIMPLE_C__
1060 #include <libxfce4util/libxfce4util-aliasdef.c>
1061