1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/paths.c for the Openbox window manager
4 Copyright (c) 2003-2007 Dana Jansens
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.
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.
16 See the COPYING file for a copy of the GNU General Public License.
19 #include "obt/bsearch.h"
20 #include "obt/paths.h"
26 #ifdef HAVE_SYS_STAT_H
27 # include <sys/stat.h>
29 #ifdef HAVE_SYS_TYPES_H
30 # include <sys/types.h>
53 GSList
*autostart_dirs
;
61 static gint
slist_path_cmp(const gchar
*a
, const gchar
*b
)
66 typedef GSList
* (*GSListFunc
) (gpointer list
, gconstpointer data
);
68 static GSList
* slist_path_add(GSList
*list
, gpointer data
, GSListFunc func
)
75 if (!g_slist_find_custom(list
, data
, (GCompareFunc
) slist_path_cmp
))
76 list
= func(list
, data
);
83 static GSList
* split_paths(const gchar
*paths
)
90 spl
= g_strsplit(paths
, ":", -1);
91 for (it
= spl
; *it
; ++it
) {
92 if ((*it
)[0]) /* skip empty strings */
93 list
= slist_path_add(list
, *it
, (GSListFunc
) g_slist_append
);
99 int gid_cmp(const void *va
, const void *vb
)
101 const gid_t a
= *(const gid_t
*)va
, b
= *(const gid_t
*)vb
;
102 return a
>b
? 1 : (a
== b
? 0 : -1);
105 static void find_uid_gid(uid_t
*u
, gid_t
**g
, guint
*n
)
115 *g
= g_new(gid_t
, *n
=1);
118 while ((gr
= getgrent())) {
119 if (gr
->gr_gid
!= (*g
)[0]) { /* skip the main group */
121 for (c
= gr
->gr_mem
; *c
; ++c
)
122 if (strcmp(*c
, name
) == 0) {
123 *g
= g_renew(gid_t
, *g
, ++(*n
)); /* save the group */
124 (*g
)[*n
-1] = gr
->gr_gid
;
131 qsort(*g
, *n
, sizeof(gid_t
), gid_cmp
);
134 ObtPaths
* obt_paths_new(void)
140 p
= g_slice_new0(ObtPaths
);
143 find_uid_gid(&p
->uid
, &p
->gid
, &p
->n_gid
);
145 path
= g_getenv("XDG_CONFIG_HOME");
146 if (path
&& path
[0] != '\0') /* not unset or empty */
147 p
->config_home
= g_build_filename(path
, NULL
);
149 p
->config_home
= g_build_filename(g_get_home_dir(), ".config", NULL
);
151 path
= g_getenv("XDG_DATA_HOME");
152 if (path
&& path
[0] != '\0') /* not unset or empty */
153 p
->data_home
= g_build_filename(path
, NULL
);
155 p
->data_home
= g_build_filename(g_get_home_dir(), ".local",
158 path
= g_getenv("XDG_CACHE_HOME");
159 if (path
&& path
[0] != '\0') /* not unset or empty */
160 p
->cache_home
= g_build_filename(path
, NULL
);
162 p
->cache_home
= g_build_filename(g_get_home_dir(), ".cache", NULL
);
164 path
= g_getenv("XDG_CONFIG_DIRS");
165 if (path
&& path
[0] != '\0') /* not unset or empty */
166 p
->config_dirs
= split_paths(path
);
168 p
->config_dirs
= slist_path_add(p
->config_dirs
,
170 (GSListFunc
) g_slist_append
);
171 p
->config_dirs
= slist_path_add(p
->config_dirs
,
175 (GSListFunc
) g_slist_append
);
177 p
->config_dirs
= slist_path_add(p
->config_dirs
,
178 g_strdup(p
->config_home
),
179 (GSListFunc
) g_slist_prepend
);
181 for (it
= p
->config_dirs
; it
; it
= g_slist_next(it
)) {
182 gchar
*const s
= g_strdup_printf("%s/autostart", (gchar
*)it
->data
);
183 p
->autostart_dirs
= g_slist_append(p
->autostart_dirs
, s
);
186 path
= g_getenv("XDG_DATA_DIRS");
187 if (path
&& path
[0] != '\0') /* not unset or empty */
188 p
->data_dirs
= split_paths(path
);
190 p
->data_dirs
= slist_path_add(p
->data_dirs
,
192 (GSListFunc
) g_slist_append
);
193 p
->data_dirs
= slist_path_add(p
->data_dirs
,
196 "usr", "local", "share", NULL
),
197 (GSListFunc
) g_slist_append
);
198 p
->data_dirs
= slist_path_add(p
->data_dirs
,
201 "usr", "share", NULL
),
202 (GSListFunc
) g_slist_append
);
204 p
->data_dirs
= slist_path_add(p
->data_dirs
,
205 g_strdup(p
->data_home
),
206 (GSListFunc
) g_slist_prepend
);
208 path
= g_getenv("PATH");
209 if (path
&& path
[0] != '\0') /* not unset or empty */
210 p
->exec_dirs
= split_paths(path
);
217 void obt_paths_ref(ObtPaths
*p
)
222 void obt_paths_unref(ObtPaths
*p
)
224 if (p
&& --p
->ref
== 0) {
227 for (it
= p
->config_dirs
; it
; it
= g_slist_next(it
))
229 g_slist_free(p
->config_dirs
);
230 for (it
= p
->data_dirs
; it
; it
= g_slist_next(it
))
232 g_slist_free(p
->data_dirs
);
233 for (it
= p
->autostart_dirs
; it
; it
= g_slist_next(it
))
235 g_slist_free(p
->autostart_dirs
);
236 for (it
= p
->exec_dirs
; it
; it
= g_slist_next(it
))
238 g_slist_free(p
->exec_dirs
);
239 g_free(p
->config_home
);
240 g_free(p
->data_home
);
241 g_free(p
->cache_home
);
244 g_slice_free(ObtPaths
, p
);
248 gchar
*obt_paths_expand_tilde(const gchar
*f
)
256 regex
= g_regex_new("(?:^|(?<=[ \\t]))~(?=[/ \\t$])", G_REGEX_MULTILINE
| G_REGEX_RAW
, 0, NULL
);
257 ret
= g_regex_replace_literal(regex
, f
, -1, 0, g_get_home_dir(), 0, NULL
);
258 g_regex_unref(regex
);
263 gboolean
obt_paths_mkdir(const gchar
*path
, gint mode
)
267 g_return_val_if_fail(path
!= NULL
, FALSE
);
268 g_return_val_if_fail(path
[0] != '\0', FALSE
);
270 if (!g_file_test(path
, G_FILE_TEST_IS_DIR
))
271 if (mkdir(path
, mode
) == -1)
277 gboolean
obt_paths_mkdir_path(const gchar
*path
, gint mode
)
281 g_return_val_if_fail(path
!= NULL
, FALSE
);
282 g_return_val_if_fail(path
[0] == '/', FALSE
);
284 if (!g_file_test(path
, G_FILE_TEST_IS_DIR
)) {
289 while ((e
= strchr(e
+ 1, '/'))) {
291 if (!(ret
= obt_paths_mkdir(c
, mode
)))
292 goto parse_mkdir_path_end
;
295 ret
= obt_paths_mkdir(c
, mode
);
297 parse_mkdir_path_end
:
304 const gchar
* obt_paths_config_home(ObtPaths
*p
)
306 return p
->config_home
;
309 const gchar
* obt_paths_data_home(ObtPaths
*p
)
314 const gchar
* obt_paths_cache_home(ObtPaths
*p
)
316 return p
->cache_home
;
319 GSList
* obt_paths_config_dirs(ObtPaths
*p
)
321 return p
->config_dirs
;
324 GSList
* obt_paths_data_dirs(ObtPaths
*p
)
329 GSList
* obt_paths_autostart_dirs(ObtPaths
*p
)
331 return p
->autostart_dirs
;
334 static inline gboolean
try_exec(const ObtPaths
*const p
,
335 const gchar
*const path
)
340 if (stat(path
, &st
) != 0)
343 if (!S_ISREG(st
.st_mode
))
345 if (st
.st_uid
== p
->uid
)
346 return st
.st_mode
& S_IXUSR
;
347 BSEARCH(guint
, p
->gid
, 0, p
->n_gid
, st
.st_gid
);
349 return st
.st_mode
& S_IXGRP
;
350 return st
.st_mode
& S_IXOTH
;
353 gboolean
obt_paths_try_exec(ObtPaths
*p
, const gchar
*path
)
355 if (path
[0] == '/') {
356 return try_exec(p
, path
);
361 for (it
= p
->exec_dirs
; it
; it
= g_slist_next(it
)) {
362 gchar
*f
= g_build_filename(it
->data
, path
, NULL
);
363 gboolean e
= try_exec(p
, f
);