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, &section, &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