diff options
| author | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2009-11-28 13:11:51 +0100 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2009-11-28 13:11:51 +0100 |
| commit | 9382f127ccebdd59917c97c61d008ed0e127cd75 (patch) | |
| tree | 0a48c8296199b343c76ef532eef014f908bc2e4d /common/filelist.c | |
| parent | 70eeaa4ec712895539ca6ecd60a42b93ec9b0904 (diff) | |
| download | tuxcmd-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.c | 382 |
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"); +} |
