Merge branch 'backport' into work
[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 };
41
42 static gint slist_path_cmp(const gchar *a, const gchar *b)
43 {
44     return strcmp(a, b);
45 }
46
47 typedef GSList* (*GSListFunc) (gpointer list, gconstpointer data);
48
49 static GSList* slist_path_add(GSList *list, gpointer data, GSListFunc func)
50 {
51     g_assert(func);
52
53     if (!data)
54         return list;
55
56     if (!g_slist_find_custom(list, data, (GCompareFunc) slist_path_cmp))
57         list = func(list, data);
58     else
59         g_free(data);
60
61     return list;
62 }
63
64 static GSList* split_paths(const gchar *paths)
65 {
66     GSList *list = NULL;
67     gchar **spl, **it;
68
69     if (!paths)
70         return NULL;
71     spl = g_strsplit(paths, ":", -1);
72     for (it = spl; *it; ++it)
73         list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
74     g_free(spl);
75     return list;
76 }
77
78 ObtPaths* obt_paths_new(void)
79 {
80     ObtPaths *p;
81     const gchar *path;
82
83     p = g_new0(ObtPaths, 1);
84     p->ref = 1;
85
86     path = g_getenv("XDG_CONFIG_HOME");
87     if (path && path[0] != '\0') /* not unset or empty */
88         p->config_home = g_build_filename(path, NULL);
89     else
90         p->config_home = g_build_filename(g_get_home_dir(), ".config", NULL);
91
92     path = g_getenv("XDG_DATA_HOME");
93     if (path && path[0] != '\0') /* not unset or empty */
94         p->data_home = g_build_filename(path, NULL);
95     else
96         p->data_home = g_build_filename(g_get_home_dir(), ".local",
97                                         "share", NULL);
98
99     path = g_getenv("XDG_CACHE_HOME");
100     if (path && path[0] != '\0') /* not unset or empty */
101         p->cache_home = g_build_filename(path, NULL);
102     else
103         p->cache_home = g_build_filename(g_get_home_dir(), ".cache", NULL);
104
105     path = g_getenv("XDG_CONFIG_DIRS");
106     if (path && path[0] != '\0') /* not unset or empty */
107         p->config_dirs = split_paths(path);
108     else {
109         p->config_dirs = slist_path_add(p->config_dirs,
110                                         g_strdup(CONFIGDIR),
111                                         (GSListFunc) g_slist_append);
112         p->config_dirs = slist_path_add(p->config_dirs,
113                                         g_build_filename
114                                         (G_DIR_SEPARATOR_S,
115                                          "etc", "xdg", NULL),
116                                         (GSListFunc) g_slist_append);
117     }
118     p->config_dirs = slist_path_add(p->config_dirs,
119                                     g_strdup(p->config_home),
120                                     (GSListFunc) g_slist_prepend);
121
122     path = g_getenv("XDG_DATA_DIRS");
123     if (path && path[0] != '\0') /* not unset or empty */
124         p->data_dirs = split_paths(path);
125     else {
126         p->data_dirs = slist_path_add(p->data_dirs,
127                                       g_strdup(DATADIR),
128                                       (GSListFunc) g_slist_append);
129         p->data_dirs = slist_path_add(p->data_dirs,
130                                       g_build_filename
131                                       (G_DIR_SEPARATOR_S,
132                                        "usr", "local", "share", NULL),
133                                       (GSListFunc) g_slist_append);
134         p->data_dirs = slist_path_add(p->data_dirs,
135                                       g_build_filename
136                                       (G_DIR_SEPARATOR_S,
137                                        "usr", "share", NULL),
138                                       (GSListFunc) g_slist_append);
139     }
140     p->data_dirs = slist_path_add(p->data_dirs,
141                                   g_strdup(p->data_home),
142                                   (GSListFunc) g_slist_prepend);
143     return p;
144 }
145
146 void obt_paths_ref(ObtPaths *p)
147 {
148     ++p->ref;
149 }
150
151 void obt_paths_unref(ObtPaths *p)
152 {
153     if (p && --p->ref == 0) {
154         GSList *it;
155
156         for (it = p->config_dirs; it; it = g_slist_next(it))
157             g_free(it->data);
158         g_slist_free(p->config_dirs);
159         for (it = p->data_dirs; it; it = g_slist_next(it))
160             g_free(it->data);
161         g_slist_free(p->data_dirs);
162         g_free(p->config_home);
163         g_free(p->data_home);
164         g_free(p->cache_home);
165
166         obt_free0(p, ObtPaths, 1);
167     }
168 }
169
170 gchar *obt_paths_expand_tilde(const gchar *f)
171 {
172     gchar *ret;
173     GRegex *regex;
174
175     if (!f)
176         return NULL;
177
178     regex = g_regex_new("(?:^|(?<=[ \\t]))~(?=[/ \\t$])", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
179     ret = g_regex_replace_literal(regex, f, -1, 0, g_get_home_dir(), 0, NULL);
180     g_regex_unref(regex);
181
182     return ret;
183 }
184
185 gboolean obt_paths_mkdir(const gchar *path, gint mode)
186 {
187     gboolean ret = TRUE;
188
189     g_return_val_if_fail(path != NULL, FALSE);
190     g_return_val_if_fail(path[0] != '\0', FALSE);
191
192     if (!g_file_test(path, G_FILE_TEST_IS_DIR))
193         if (mkdir(path, mode) == -1)
194             ret = FALSE;
195
196     return ret;
197 }
198
199 gboolean obt_paths_mkdir_path(const gchar *path, gint mode)
200 {
201     gboolean ret = TRUE;
202
203     g_return_val_if_fail(path != NULL, FALSE);
204     g_return_val_if_fail(path[0] == '/', FALSE);
205
206     if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
207         gchar *c, *e;
208
209         c = g_strdup(path);
210         e = c;
211         while ((e = strchr(e + 1, '/'))) {
212             *e = '\0';
213             if (!(ret = obt_paths_mkdir(c, mode)))
214                 goto parse_mkdir_path_end;
215             *e = '/';
216         }
217         ret = obt_paths_mkdir(c, mode);
218
219     parse_mkdir_path_end:
220         g_free(c);
221     }
222
223     return ret;
224 }
225
226 const gchar* obt_paths_config_home(ObtPaths *p)
227 {
228     return p->config_home;
229 }
230
231 const gchar* obt_paths_data_home(ObtPaths *p)
232 {
233     return p->data_home;
234 }
235
236 const gchar* obt_paths_cache_home(ObtPaths *p)
237 {
238     return p->cache_home;
239 }
240
241 GSList* obt_paths_config_dirs(ObtPaths *p)
242 {
243     return p->config_dirs;
244 }
245
246 GSList* obt_paths_data_dirs(ObtPaths *p)
247 {
248     return p->data_dirs;
249 }