See the COPYING file for a copy of the GNU General Public License.
*/
+#include "obt/bsearch.h"
#include "obt/paths.h"
#include "obt/util.h"
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_GRP_H
+# include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
struct _ObtPaths
{
gchar *cache_home;
GSList *config_dirs;
GSList *data_dirs;
+ GSList *autostart_dirs;
+ GSList *exec_dirs;
+
+ uid_t uid;
+ gid_t *gid;
+ guint n_gid;
};
static gint slist_path_cmp(const gchar *a, const gchar *b)
return list;
}
+int gid_cmp(const void *va, const void *vb)
+{
+ const gid_t a = *(const gid_t*)va, b = *(const gid_t*)vb;
+ return a>b ? 1 : (a == b ? 0 : -1);
+}
+
+static void find_uid_gid(uid_t *u, gid_t **g, guint *n)
+{
+ struct passwd *pw;
+ const gchar *name;
+ struct group *gr;
+
+ *u = getuid();
+ pw = getpwuid(*u);
+ name = pw->pw_name;
+
+ *g = g_new(gid_t, *n=1);
+ (*g)[0] = getgid();
+
+ while ((gr = getgrent())) {
+ if (gr->gr_gid != (*g)[0]) { /* skip the main group */
+ gchar **c;
+ for (c = gr->gr_mem; *c; ++c)
+ if (strcmp(*c, name) == 0) {
+ *g = g_renew(gid_t, *g, ++(*n)); /* save the group */
+ (*g)[*n-1] = gr->gr_gid;
+ break;
+ }
+ }
+ }
+ endgrent();
+
+ qsort(*g, sizeof(gid_t), *n, gid_cmp);
+}
+
ObtPaths* obt_paths_new(void)
{
ObtPaths *p;
const gchar *path;
+ GSList *it;
- p = g_new0(ObtPaths, 1);
+ p = g_slice_new0(ObtPaths);
p->ref = 1;
+ find_uid_gid(&p->uid, &p->gid, &p->n_gid);
+
path = g_getenv("XDG_CONFIG_HOME");
if (path && path[0] != '\0') /* not unset or empty */
p->config_home = g_build_filename(path, NULL);
g_strdup(p->config_home),
(GSListFunc) g_slist_prepend);
+ for (it = p->config_dirs; it; it = g_slist_next(it)) {
+ gchar *const s = g_strdup_printf("%s/autostart", (gchar*)it->data);
+ p->autostart_dirs = g_slist_append(p->autostart_dirs, s);
+ }
+
path = g_getenv("XDG_DATA_DIRS");
if (path && path[0] != '\0') /* not unset or empty */
p->data_dirs = split_paths(path);
p->data_dirs = slist_path_add(p->data_dirs,
g_strdup(p->data_home),
(GSListFunc) g_slist_prepend);
+
+ path = g_getenv("PATH");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->exec_dirs = split_paths(path);
+ else
+ p->exec_dirs = NULL;
+
return p;
}
for (it = p->data_dirs; it; it = g_slist_next(it))
g_free(it->data);
g_slist_free(p->data_dirs);
+ for (it = p->autostart_dirs; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(p->autostart_dirs);
g_free(p->config_home);
g_free(p->data_home);
g_free(p->cache_home);
- obt_free0(p, ObtPaths, 1);
+ g_slice_free(ObtPaths, p);
}
}
gchar *obt_paths_expand_tilde(const gchar *f)
{
- gchar **spl;
gchar *ret;
+ GRegex *regex;
if (!f)
return NULL;
- spl = g_strsplit(f, "~", 0);
- ret = g_strjoinv(g_get_home_dir(), spl);
- g_strfreev(spl);
+
+ regex = g_regex_new("(?:^|(?<=[ \\t]))~(?=[/ \\t$])", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
+ ret = g_regex_replace_literal(regex, f, -1, 0, g_get_home_dir(), 0, NULL);
+ g_regex_unref(regex);
+
return ret;
}
{
return p->data_dirs;
}
+
+GSList* obt_paths_autostart_dirs(ObtPaths *p)
+{
+ return p->autostart_dirs;
+}
+
+static inline gboolean try_exec(const ObtPaths *const p,
+ const gchar *const path)
+{
+ struct stat st;
+ BSEARCH_SETUP(guint);
+
+ stat(path, &st);
+
+ if (!S_ISREG(st.st_mode))
+ return FALSE;
+ if (st.st_uid == p->uid)
+ return st.st_mode & S_IXUSR;
+ BSEARCH(guint, p->gid, 0, p->n_gid, st.st_gid);
+ if (BSEARCH_FOUND())
+ return st.st_mode & S_IXGRP;
+ return st.st_mode & S_IXOTH;
+}
+
+gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path)
+{
+ if (path[0] == '/') {
+ return try_exec(p, path);
+ }
+ else {
+ GSList *it;
+
+ for (it = p->exec_dirs; it; it = g_slist_next(it)) {
+ gchar *f = g_strdup_printf(it->data, G_DIR_SEPARATOR_S, path);
+ gboolean e = try_exec(p, f);
+ g_free(f);
+ if (e) return TRUE;
+ }
+ }
+
+ return FALSE;
+}