/* * This file is part of Cockpit. * * Copyright (C) 2014 Red Hat, Inc. * * Cockpit is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * Cockpit is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ #include "config.h" #include "cockpitpaths.h" #include gboolean cockpit_path_has_parent (const gchar *path, const gchar *parent) { gsize length = strlen (parent); const gchar *last; if (length == 1 && parent[0] == '/' && path[0]) last = path + 1; else if (strncmp (path, parent, length) == 0 && path[length] == '/') last = path + length + 1; else return FALSE; return strchr (last, '/') == NULL; } gboolean cockpit_path_equal_or_ancestor (const gchar *path, const gchar *ancestor) { gsize length = strlen (ancestor); if (length == 1 && ancestor[0] == '/') return TRUE; if (strncmp (path, ancestor, length) == 0 && (path[length] == '/' || path[length] == '\0')) return TRUE; return FALSE; } gboolean cockpit_path_has_ancestor (const gchar *path, const gchar *ancestor) { gsize length = strlen (ancestor); if (length == 1 && ancestor[0] == '/') return TRUE; if (strncmp (path, ancestor, length) == 0 && path[length] == '/') return TRUE; return FALSE; } typedef struct { gsize len; const gchar *data; } PathData; static gint tree_path_cmp (gconstpointer a, gconstpointer b, gpointer user_data) { const PathData *pa = a; const PathData *pb = b; gsize la = pa->len; gsize lb = pb->len; gint ret; ret = memcmp (pa->data, pb->data, MIN (la, lb)); if (ret == 0 && la != lb) ret = (la < lb) ? -1 : 1; return ret; } static gint tree_prefix_search (gconstpointer a, gconstpointer b) { const PathData *pa = a; const PathData *pb = b; gsize la = pa->len; gsize lb = pb->len; gint ret; /* Yes, g_tree_search() means this is backwards */ ret = memcmp (pb->data, pa->data, MIN (la, lb)); if (ret == 0 && la != lb) { if (la > lb) { if ((lb == 1 && pb->data[0] == '/') || pa->data[lb] == '/') ret = 0; else ret = -1; } else { ret = 1; } } return ret; } GTree * cockpit_paths_new (void) { return g_tree_new_full (tree_path_cmp, NULL, g_free, NULL); } /* * cockpit_paths_add: * @tree: The tree to add to * @path: The path to add * * Adds the path if this path or a parent is not already * in the tree. Will return %NULL if the path is already * in the tree ... otherwise will return the internally * reallocated path. */ const gchar * cockpit_paths_add (GTree *tree, const gchar *path) { PathData key = { strlen (path), path }; PathData *pd = g_tree_lookup (tree, &key); if (!pd) { pd = g_malloc (sizeof (PathData) + key.len + 1); pd->len = key.len; pd->data = (gchar *)(pd + 1); memcpy ((gchar *)pd->data, path, key.len + 1); g_tree_replace (tree, pd, pd); return pd->data; } return NULL; } gboolean cockpit_paths_remove (GTree *tree, const gchar *path) { PathData key = { strlen (path), path }; return g_tree_remove (tree, &key); } const gchar * cockpit_paths_contain (GTree *tree, const gchar *path) { PathData key = { strlen(path), path }; PathData *pd = g_tree_lookup (tree, &key); return pd ? pd->data : NULL; } gboolean cockpit_paths_contain_or_descendant (GTree *tree, const gchar *path) { PathData key = { strlen (path), path }; PathData *pd = g_tree_search (tree, tree_prefix_search, &key); return pd != NULL; } const gchar * cockpit_paths_contain_or_ancestor (GTree *tree, const gchar *path) { PathData key = { strlen (path), path }; gboolean last = FALSE; const gchar *pos; PathData *pd; for (;;) { pd = g_tree_lookup (tree, &key); if (pd) return pd->data; if (last) return NULL; pos = memrchr (path, '/', key.len); if (!pos) return NULL; if (path == pos) { key.len = 1; last = TRUE; } else { key.len = (pos - path); } } }