summaryrefslogtreecommitdiff
path: root/common/filelist.c
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2009-11-28 13:11:51 +0100
committerTomas Bzatek <tbzatek@users.sourceforge.net>2009-11-28 13:11:51 +0100
commit9382f127ccebdd59917c97c61d008ed0e127cd75 (patch)
tree0a48c8296199b343c76ef532eef014f908bc2e4d /common/filelist.c
parent70eeaa4ec712895539ca6ecd60a42b93ec9b0904 (diff)
downloadtuxcmd-modules-0.6.72.tar.xz
Engine and VFS API cleanupv0.6.72
Also enable symlink resolving by default.
Diffstat (limited to 'common/filelist.c')
-rw-r--r--common/filelist.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/common/filelist.c b/common/filelist.c
new file mode 100644
index 0000000..cae250e
--- /dev/null
+++ b/common/filelist.c
@@ -0,0 +1,382 @@
+/* Tux Commander VFS: Filelist tree storage
+ * Copyright (C) 2007-2009 Tomas Bzatek <tbzatek@users.sourceforge.net>
+ * Check for updates on tuxcmd.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "filelist.h"
+#include "strutils.h"
+#include "vfsutils.h"
+
+
+
+struct PathTree *
+filelist_tree_new ()
+{
+ struct PathTree *tree;
+
+ tree = g_malloc0 (sizeof (struct PathTree));
+ log ("filelist_tree_new()\n");
+ tree->items = g_ptr_array_new ();
+ tree->data = NULL;
+ tree->original_index = 0;
+ tree->node = g_strdup ("/");
+ tree->original_pathstr = NULL;
+
+ /* create placeholder data */
+ tree->data = create_placeholder_item (tree->node, vDirectory);
+
+ return tree;
+}
+
+void
+filelist_tree_free (struct PathTree *tree)
+{
+ int i;
+
+ if (! tree) {
+ fprintf (stderr, "filelist_tree_free: tree == NULL !\n");
+ return;
+ }
+
+ if (tree->items && tree->items->len > 0) {
+ for (i = 0; i < tree->items->len; i++)
+ filelist_tree_free (g_ptr_array_index (tree->items, i));
+ }
+ if (tree->items)
+ g_ptr_array_free (tree->items, TRUE);
+ free_vfs_item (tree->data);
+ g_free (tree->node);
+ g_free (tree->original_pathstr);
+ g_free (tree);
+}
+
+
+/* -------------------------------------------------------------------------------------- */
+
+static void
+filelist_tree_print_recurr (struct PathTree *tree, int level)
+{
+ char *fill;
+ int i;
+ struct PathTree* t;
+
+ if (tree) {
+ fill = g_strnfill (level * 2, 32);
+ g_print (" %s#%lu. \"%s\"\n", fill, tree->original_index, tree->node);
+
+ if (tree->items && tree->items->len > 0)
+ for (i = 0; i < tree->items->len; i++) {
+ t = g_ptr_array_index (tree->items, i);
+ filelist_tree_print_recurr (t, level + 1);
+ }
+ g_free (fill);
+ }
+}
+
+void
+filelist_tree_print (struct PathTree *tree)
+{
+ filelist_tree_print_recurr (tree, 0);
+}
+
+
+/* -------------------------------------------------------------------------------------- */
+
+struct PathTree *
+filelist_tree_find_node_by_path (struct PathTree *tree, const char *path)
+{
+ char *p;
+ char *first_part;
+ char *last_part;
+ struct PathTree *node;
+ struct PathTree *t;
+ unsigned int i;
+
+ /* remove leading './' */
+ if (strstr (path, "./") == path)
+ path += 2;
+ /* remove leading and trailing '/' if present */
+ p = exclude_trailing_path_sep (path + G_IS_DIR_SEPARATOR (*path));
+ log (" filelist_tree_find_node_by_path: path = '%s', p = '%s'\n", path, p);
+
+ node = NULL;
+ if (tree && tree->node && g_strcmp0 (tree->node, "/") == 0 && g_strcmp0 (path, "/") == 0) {
+ log (" filelist_tree_find_node_by_path: matched root '/' element, returning.\n");
+ node = tree;
+ } else
+ if (tree && tree->items && tree->items->len > 0) {
+ split_path (p, &first_part, &last_part);
+ log (" filelist_tree_find_node_by_path: pos = '%s'\n", pos);
+ log (" filelist_tree_find_node_by_path: first_part = '%s'\n", first_part);
+ log (" filelist_tree_find_node_by_path: last_part = '%s'\n", last_part);
+
+ /* find existing node */
+ for (i = 0; i < tree->items->len; i++) {
+ t = g_ptr_array_index (tree->items, i);
+ if (g_strcmp0 (t->node, first_part) == 0) {
+ /* this is the final station */
+ if (! last_part) {
+ node = t;
+ log (" filelist_tree_find_node_by_path: found final node '%s', returning.\n", t->node);
+ } else
+
+ /* recurse to child items */
+ if (t->items && last_part) {
+ log (" filelist_tree_find_node_by_path: found node '%s', recursing in deep.\n", t->node);
+ node = filelist_tree_find_node_by_path (t, last_part);
+ } else {
+ /* item found but no subitems */
+ log (" filelist_tree_find_node_by_path: found node '%s', but no subitems found.\n", t->node);
+ }
+
+ break;
+ }
+ }
+ g_free (first_part);
+ g_free (last_part);
+ }
+ g_free (p);
+ return node;
+}
+
+unsigned long int
+filelist_find_original_index_by_path (struct PathTree *tree, const char *path)
+{
+ struct PathTree *node;
+
+ node = filelist_tree_find_node_by_path (tree, path);
+ return node ? node->original_index : 0;
+}
+
+
+/* -------------------------------------------------------------------------------------- */
+
+static void
+filelist_tree_add_item_recurr (struct PathTree *tree, const char *path, struct TVFSItem *item, const char *original_pathstr, unsigned long int original_index)
+{
+ char *first_part;
+ char *last_part;
+ struct PathTree *t;
+ struct PathTree *node;
+ unsigned int i;
+
+ split_path (path, &first_part, &last_part);
+ log (" filelist_tree_add_item_recur: pos = '%s'\n", pos);
+ log (" filelist_tree_add_item_recur: first_part = '%s'\n", first_part);
+ log (" filelist_tree_add_item_recur: last_part = '%s'\n", last_part);
+
+ if (last_part == NULL) {
+ /* final destination, create new item here */
+ log (" filelist_tree_add_item_recur: creating new item here.\n");
+ t = g_malloc0 (sizeof (struct PathTree));
+ t->items = NULL;
+ t->data = item;
+ t->node = g_strdup (first_part);
+ t->original_index = original_index;
+ t->original_pathstr = g_strdup (original_pathstr);
+ if (t->data) {
+ g_free (t->data->FName);
+ g_free (t->data->FDisplayName);
+ t->data->FName = g_strdup (path);
+ t->data->FDisplayName = g_filename_display_name (path);
+ }
+ /* create new list of subitems and add new item */
+ if (! tree->items)
+ tree->items = g_ptr_array_new ();
+ g_ptr_array_add (tree->items, t);
+ } else {
+ /* not a final place, find parent node or create new one if doesn't exist */
+ log (" filelist_tree_add_item_recur: node '%s', path '%s'\n", tree->node, path);
+ if (! tree->items)
+ tree->items = g_ptr_array_new ();
+
+ node = NULL;
+ if (tree->items->len > 0)
+ for (i = 0; i < tree->items->len; i++) {
+ t = g_ptr_array_index (tree->items, i);
+ if (g_strcmp0 (t->node, first_part) == 0) {
+ log (" filelist_tree_add_item_recur: found node '%s'\n", t->node);
+ node = t;
+ break;
+ }
+ }
+
+ if (node == NULL) {
+ /* create new path holder node */
+ log (" filelist_tree_add_item_recur: parent node not found, creating new path holder\n");
+ node = g_malloc0 (sizeof (struct PathTree));
+ node->items = g_ptr_array_new ();
+ node->node = g_strdup (first_part);
+ node->original_index = 0;
+ node->original_pathstr = NULL;
+
+ /* create placeholder data */
+ node->data = create_placeholder_item (node->node, vDirectory);
+ g_ptr_array_add (tree->items, node);
+ }
+
+ /* and recurse one level deeper */
+ filelist_tree_add_item_recurr (node, last_part, item, original_pathstr, original_index);
+ }
+ g_free (first_part);
+ g_free (last_part);
+}
+
+
+gboolean
+filelist_tree_add_item (struct PathTree *tree, const char *path, struct TVFSItem *item, const char *original_pathstr, unsigned long int original_index)
+{
+ char *p;
+ char *pp;
+ struct PathTree *found;
+
+ if (! tree) {
+ fprintf (stderr, "filelist_tree_add_item: tree == NULL !\n");
+ return FALSE;
+ }
+ if (! path) {
+ fprintf (stderr, "filelist_tree_add_item: path == NULL !\n");
+ return FALSE;
+ }
+
+ if (g_strcmp0 (path, "/") == 0 || g_strcmp0 (path, ".") == 0 || g_strcmp0 (path, "..") == 0 || g_strcmp0 (path, "./") == 0) {
+ fprintf (stderr, "filelist_tree_add_item: path '%s' is not a valid path\n", path);
+ return FALSE;
+ }
+
+ /* remove leading './' */
+ if (g_strstr_len (path, -1, "./") == path)
+ path += 2;
+
+ /* remove leading and trailing '/' if present */
+ p = exclude_trailing_path_sep (path + G_IS_DIR_SEPARATOR (*path));
+ log (" filelist_tree_add_item: p = '%s'\n", p);
+
+ pp = canonicalize_filename (p);
+ if (! pp)
+ pp = g_strdup (p);
+
+ found = filelist_tree_find_node_by_path (tree, pp);
+ if (found) {
+ /* replace old data with current ones (record might have been created automatically during path building) */
+ log (" filelist_tree_add_item: found old item, replacing data\n");
+ found->original_index = original_index;
+ /* free old data */
+ free_vfs_item (found->data);
+ found->data = item;
+ if (found->data) {
+ g_free (found->data->FName);
+ g_free (found->data->FDisplayName);
+ found->data->FName = g_strdup (found->node);
+ found->data->FDisplayName = g_filename_display_name (found->node);
+ }
+ } else {
+ /* create new item recursively */
+ filelist_tree_add_item_recurr (tree, pp, item, original_pathstr, original_index);
+ }
+
+ g_free (p);
+ g_free (pp);
+ return TRUE;
+}
+
+
+/* -------------------------------------------------------------------------------------- */
+
+struct PathTree *
+filelist_tree_get_item_by_index (struct PathTree *tree, unsigned long int index)
+{
+ struct PathTree *t = NULL;
+
+ if (tree && tree->items && tree->items->len > index)
+ t = g_ptr_array_index (tree->items, index);
+ return t;
+}
+
+
+/* -------------------------------------------------------------------------------------- */
+
+static void
+resolve_symlink_recurr (struct PathTree *node, struct PathTree *root_tree, const char *path, const char *link_dest, int level)
+{
+ char *rel;
+ char *new_path;
+ struct PathTree *link_t;
+
+ rel = resolve_relative (path, link_dest);
+ g_print (" resolve_symlink_recurr: relative = '%s'\n", rel);
+ if (rel) {
+ link_t = filelist_tree_find_node_by_path (root_tree, rel);
+ if (link_t && link_t->data) {
+ if (link_t->data->IsLink) {
+ /* another symlink, recurse into deep */
+ new_path = g_path_get_dirname (rel);
+ if (level > FILELIST_MAX_SYMLINK_DEPTH || ! new_path) {
+ node->symlink_target_data = link_t->data;
+ g_print (" resolve_symlink_recurr: max depth reached, link is broken. new_path = '%s'\n", new_path);
+ } else {
+ g_print (" resolve_symlink_recurr: going deep, rel = '%s', new_path = '%s'\n", rel, new_path);
+ resolve_symlink_recurr (node, root_tree, new_path, link_t->data->sLinkTo, level + 1);
+ }
+ g_free (new_path);
+ } else {
+ /* final destination */
+ node->symlink_target_data = link_t->data;
+ g_print (" resolve_symlink_recurr: reached target '%s'\n", rel);
+ }
+ } else {
+ /* target not found, link is broken */
+ node->symlink_target_data = NULL;
+ g_print (" resolve_symlink_recurr: target '%s' not found, link is broken\n", rel);
+ }
+ }
+ g_free (rel);
+}
+
+static void
+filelist_tree_resolve_symlinks_recurr (struct PathTree *tree, struct PathTree *root_tree, const char *path)
+{
+ struct PathTree *t;
+ char *new_path;
+ unsigned int i;
+
+ if (tree && tree->items && tree->items->len > 0) {
+ for (i = 0; i < tree->items->len; i++) {
+ t = g_ptr_array_index (tree->items, i);
+
+ if (t && t->data && t->data->IsLink && t->data->sLinkTo) {
+ g_print ("filelist_tree_resolve_symlinks: found '%s/%s' --> '%s', resolving...\n", path, t->node, t->data->sLinkTo);
+ resolve_symlink_recurr (t, root_tree, path, t->data->sLinkTo, 1);
+ }
+ new_path = g_build_filename (path, t->node, NULL);
+ filelist_tree_resolve_symlinks_recurr (t, root_tree, new_path);
+ g_free (new_path);
+ }
+ }
+}
+
+void
+filelist_tree_resolve_symlinks (struct PathTree *tree)
+{
+ log ("filelist_tree_resolve_symlinks: begin\n");
+ filelist_tree_resolve_symlinks_recurr (tree, tree, "/");
+ log ("filelist_tree_resolve_symlinks: end\n");
+}