/* Tux Commander VFS: Filelist tree storage * Copyright (C) 2007-2009 Tomas Bzatek * 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 #include #include #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"); }