parse key/value pairs from the .desktop file and save them in a hashtable
[dana/openbox.git] / obt / paths.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    obt/paths.c for the Openbox window manager
4    Copyright (c) 2003-2007   Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program 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
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "obt/paths.h"
20 #include "obt/util.h"
21
22 #ifdef HAVE_SYS_STAT_H
23 #  include <sys/stat.h>
24 #endif
25 #ifdef HAVE_SYS_TYPES_H
26 #  include <sys/types.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #  include <string.h>
30 #endif
31
32 struct _ObtPaths
33 {
34     gint   ref;
35     gchar  *config_home;
36     gchar  *data_home;
37     gchar  *cache_home;
38     GSList *config_dirs;
39     GSList *data_dirs;
40     GSList *autostart_dirs;
41 };
42
43 static gint slist_path_cmp(const gchar *a, const gchar *b)
44 {
45     return strcmp(a, b);
46 }
47
48 typedef GSList* (*GSListFunc) (gpointer list, gconstpointer data);
49
50 static GSList* slist_path_add(GSList *list, gpointer data, GSListFunc func)
51 {
52     g_assert(func);
53
54     if (!data)
55         return list;
56
57     if (!g_slist_find_custom(list, data, (GCompareFunc) slist_path_cmp))
58         list = func(list, data);
59     else
60         g_free(data);
61
62     return list;
63 }
64
65 static GSList* split_paths(const gchar *paths)
66 {
67     GSList *list = NULL;
68     gchar **spl, **it;
69
70     if (!paths)
71         return NULL;
72     spl = g_strsplit(paths, ":", -1);
73     for (it = spl; *it; ++it)
74         list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
75     g_free(spl);
76     return list;
77 }
78
79 ObtPaths* obt_paths_new(void)
80 {
81     ObtPaths *p;
82     const gchar *path;
83     GSList *it;
84
85     p = g_slice_new0(ObtPaths);
86     p->ref = 1;
87
88     path = g_getenv("XDG_CONFIG_HOME");
89     if (path && path[0] != '\0') /* not unset or empty */
90         p->config_home = g_build_filename(path, NULL);
91     else
92         p->config_home = g_build_filename(g_get_home_dir(), ".config", NULL);
93
94     path = g_getenv("XDG_DATA_HOME");
95     if (path && path[0] != '\0') /* not unset or empty */
96         p->data_home = g_build_filename(path, NULL);
97     else
98         p->data_home = g_build_filename(g_get_home_dir(), ".local",
99                                         "share", NULL);
100
101     path = g_getenv("XDG_CACHE_HOME");
102     if (path && path[0] != '\0') /* not unset or empty */
103         p->cache_home = g_build_filename(path, NULL);
104     else
105         p->cache_home = g_build_filename(g_get_home_dir(), ".cache", NULL);
106
107     path = g_getenv("XDG_CONFIG_DIRS");
108     if (path && path[0] != '\0') /* not unset or empty */
109         p->config_dirs = split_paths(path);
110     else {
111         p->config_dirs = slist_path_add(p->config_dirs,
112                                         g_strdup(CONFIGDIR),
113                                         (GSListFunc) g_slist_append);
114         p->config_dirs = slist_path_add(p->config_dirs,
115                                         g_build_filename
116                                         (G_DIR_SEPARATOR_S,
117                                          "etc", "xdg", NULL),
118                                         (GSListFunc) g_slist_append);
119     }
120     p->config_dirs = slist_path_add(p->config_dirs,
121                                     g_strdup(p->config_home),
122                                     (GSListFunc) g_slist_prepend);
123
124     for (it = p->config_dirs; it; it = g_slist_next(it)) {
125         gchar *const s = g_strdup_printf("%s/autostart", (gchar*)it->data);
126         p->autostart_dirs = g_slist_append(p->autostart_dirs, s);
127     }
128
129     path = g_getenv("XDG_DATA_DIRS");
130     if (path && path[0] != '\0') /* not unset or empty */
131         p->data_dirs = split_paths(path);
132     else {
133         p->data_dirs = slist_path_add(p->data_dirs,
134                                       g_strdup(DATADIR),
135                                       (GSListFunc) g_slist_append);
136         p->data_dirs = slist_path_add(p->data_dirs,
137                                       g_build_filename
138                                       (G_DIR_SEPARATOR_S,
139                                        "usr", "local", "share", NULL),
140                                       (GSListFunc) g_slist_append);
141         p->data_dirs = slist_path_add(p->data_dirs,
142                                       g_build_filename
143                                       (G_DIR_SEPARATOR_S,
144                                        "usr", "share", NULL),
145                                       (GSListFunc) g_slist_append);
146     }
147     p->data_dirs = slist_path_add(p->data_dirs,
148                                   g_strdup(p->data_home),
149                                   (GSListFunc) g_slist_prepend);
150     return p;
151 }
152
153 void obt_paths_ref(ObtPaths *p)
154 {
155     ++p->ref;
156 }
157
158 void obt_paths_unref(ObtPaths *p)
159 {
160     if (p && --p->ref == 0) {
161         GSList *it;
162
163         for (it = p->config_dirs; it; it = g_slist_next(it))
164             g_free(it->data);
165         g_slist_free(p->config_dirs);
166         for (it = p->data_dirs; it; it = g_slist_next(it))
167             g_free(it->data);
168         g_slist_free(p->data_dirs);
169         for (it = p->autostart_dirs; it; it = g_slist_next(it))
170             g_free(it->data);
171         g_slist_free(p->autostart_dirs);
172         g_free(p->config_home);
173         g_free(p->data_home);
174         g_free(p->cache_home);
175
176         g_slice_free(ObtPaths, p);
177     }
178 }
179
180 gchar *obt_paths_expand_tilde(const gchar *f)
181 {
182     gchar *ret;
183     GRegex *regex;
184
185     if (!f)
186         return NULL;
187
188     regex = g_regex_new("(?:^|(?<=[ \\t]))~(?=[/ \\t$])", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
189     ret = g_regex_replace_literal(regex, f, -1, 0, g_get_home_dir(), 0, NULL);
190     g_regex_unref(regex);
191
192     return ret;
193 }
194
195 gboolean obt_paths_mkdir(const gchar *path, gint mode)
196 {
197     gboolean ret = TRUE;
198
199     g_return_val_if_fail(path != NULL, FALSE);
200     g_return_val_if_fail(path[0] != '\0', FALSE);
201
202     if (!g_file_test(path, G_FILE_TEST_IS_DIR))
203         if (mkdir(path, mode) == -1)
204             ret = FALSE;
205
206     return ret;
207 }
208
209 gboolean obt_paths_mkdir_path(const gchar *path, gint mode)
210 {
211     gboolean ret = TRUE;
212
213     g_return_val_if_fail(path != NULL, FALSE);
214     g_return_val_if_fail(path[0] == '/', FALSE);
215
216     if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
217         gchar *c, *e;
218
219         c = g_strdup(path);
220         e = c;
221         while ((e = strchr(e + 1, '/'))) {
222             *e = '\0';
223             if (!(ret = obt_paths_mkdir(c, mode)))
224                 goto parse_mkdir_path_end;
225             *e = '/';
226         }
227         ret = obt_paths_mkdir(c, mode);
228
229     parse_mkdir_path_end:
230         g_free(c);
231     }
232
233     return ret;
234 }
235
236 const gchar* obt_paths_config_home(ObtPaths *p)
237 {
238     return p->config_home;
239 }
240
241 const gchar* obt_paths_data_home(ObtPaths *p)
242 {
243     return p->data_home;
244 }
245
246 const gchar* obt_paths_cache_home(ObtPaths *p)
247 {
248     return p->cache_home;
249 }
250
251 GSList* obt_paths_config_dirs(ObtPaths *p)
252 {
253     return p->config_dirs;
254 }
255
256 GSList* obt_paths_data_dirs(ObtPaths *p)
257 {
258     return p->data_dirs;
259 }
260
261 GSList* obt_paths_autostart_dirs(ObtPaths *p)
262 {
263     return p->autostart_dirs;
264 }