/* Tux Commander VFS: String utilities * Copyright (C) 2007-2008 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 #include #include #include "treepathutils.h" #include "strutils.h" #include "vfsutils.h" struct PathTree* filelist_tree_new() { struct PathTree *tree = (struct PathTree*)malloc(sizeof(struct PathTree)); memset(tree, 0, sizeof(struct PathTree)); log("filelist_tree_new()\n"); tree->items = g_ptr_array_new(); tree->data = NULL; tree->index = 0; tree->node = strdup("/"); tree->original_pathstr = NULL; // create placeholder data tree->data = (struct TVFSItem*)malloc(sizeof(struct TVFSItem)); memset(tree->data, 0, sizeof(struct TVFSItem)); tree->data->FName = strdup(tree->node); tree->data->FDisplayName = strdup(tree->node); tree->data->ItemType = vDirectory; tree->data->iMode = S_IRWXO + S_IRWXG + S_IRWXU; tree->data->iUID = geteuid(); tree->data->iGID = getegid(); tree->data->m_time = time(NULL); tree->data->c_time = tree->data->m_time; tree->data->a_time = tree->data->m_time; return tree; } void filelist_tree_free(struct PathTree *tree) { if (! tree) { fprintf(stderr, "filelist_tree_free: tree == NULL !\n"); return; } if (tree) { if ((tree->items) && (tree->items->len > 0)) { unsigned int i; for (i = 0; i < tree->items->len; i++) filelist_tree_free((struct PathTree*)g_ptr_array_index(tree->items, i)); } if (tree->items) g_ptr_array_free(tree->items, TRUE); if (tree->data) { if (tree->data->FName) free(tree->data->FName); if (tree->data->FDisplayName) free(tree->data->FDisplayName); if (tree->data->sLinkTo) free(tree->data->sLinkTo); free(tree->data); } if (tree->node) free(tree->node); if (tree->original_pathstr) free(tree->original_pathstr); free(tree); } } void filelist_tree_print_recurr(struct PathTree *tree, int level) { if (tree) { char *fill = g_strnfill(level * 2, 32); printf(" %s#%lu. \"%s\"\n", fill, tree->index, tree->node); if ((tree->items) && (tree->items->len > 0)) { unsigned int i; for (i = 0; i < tree->items->len; i++) { struct PathTree* t = (struct PathTree*)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) { // remove leading './' if (strstr(path, "./") == path) path += 2; // remove leading and trailing '/' if present char *p; if (IS_DIR_SEP(*path)) p = exclude_trailing_path_sep(path + 1); else p = exclude_trailing_path_sep(path); log(" filelist_tree_find_node_by_path: path = '%s', p = '%s'\n", path, p); // log("xx: '%s', '%s'\n", tree->node, path); struct PathTree* node = NULL; if ((tree) && (tree->node) && (strcmp(tree->node, "/") == 0) && (strcmp(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 char *pos = strstr(p, "/"); char *first_part; char *last_part; if (pos) { first_part = strndup(p, pos - p); if (strlen(pos + 1) > 0) last_part = strdup(pos + 1); else last_part = NULL; } else { first_part = strdup(p); last_part = NULL; } 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 unsigned int i; for (i = 0; i < tree->items->len; i++) { struct PathTree* t = (struct PathTree*)g_ptr_array_index(tree->items, i); if (strcmp(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; } } free(first_part); free(last_part); } free(p); return node; } unsigned long int filelist_find_index_by_path(struct PathTree *tree, const char *path) { struct PathTree* node = filelist_tree_find_node_by_path(tree, path); if (node) return node->index; else return 0; } void filelist_tree_add_item_recurr(struct PathTree *tree, const char *path, const char *original_pathstr, struct TVFSItem *item, unsigned long index) { char *pos = strstr(path, "/"); char *first_part; char *last_part; if (pos) { first_part = strndup(path, pos - path); if (strlen(pos + 1) > 0) last_part = strdup(pos + 1); else last_part = NULL; } else { first_part = strdup(path); last_part = NULL; } 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) { // final destination, create new item here log(" filelist_tree_add_item_recur: creating new item here.\n"); struct PathTree *t = (struct PathTree*)malloc(sizeof(struct PathTree)); memset(t, 0, sizeof(struct PathTree)); t->items = NULL; t->data = item; t->index = index; t->node = strdup(path); if (original_pathstr) t->original_pathstr = strdup(original_pathstr); if (t->data) t->data->FName = strdup(path); if (t->data) t->data->FDisplayName = strdup(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 { // 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(); struct PathTree *node = NULL; if (tree->items->len > 0) { unsigned int i; for (i = 0; i < tree->items->len; i++) { struct PathTree* t = (struct PathTree*)g_ptr_array_index(tree->items, i); if (strcmp(t->node, first_part) == 0) { log(" filelist_tree_add_item_recur: found node '%s'\n", t->node); node = t; break; } } } // create new path holder node if (! node) { log(" filelist_tree_add_item_recur: parent node not found, creating new path holder\n"); node = (struct PathTree*)malloc(sizeof(struct PathTree)); memset(node, 0, sizeof(struct PathTree)); node->items = g_ptr_array_new(); node->index = 0; node->node = strdup(first_part); node->original_pathstr = NULL; // create placeholder data node->data = (struct TVFSItem*)malloc(sizeof(struct TVFSItem)); memset(node->data, 0, sizeof(struct TVFSItem)); node->data->FName = strdup(node->node); node->data->FDisplayName = strdup(node->node); node->data->ItemType = vDirectory; node->data->iMode = S_IRWXO + S_IRWXG + S_IRWXU; node->data->iUID = geteuid(); node->data->iGID = getegid(); node->data->m_time = time(NULL); node->data->c_time = node->data->m_time; node->data->a_time = node->data->m_time; g_ptr_array_add(tree->items, node); } // and recurse one level deeper filelist_tree_add_item_recurr(node, last_part, original_pathstr, item, index); } free(first_part); free(last_part); } gboolean filelist_tree_add_item(struct PathTree *tree, const char *path, const char *original_pathstr, struct TVFSItem *item, unsigned long index) { 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 ((strcmp(path, "/") == 0) || (strcmp(path, ".") == 0) || (strcmp(path, "..") == 0) || (strcmp(path, "./") == 0)) { fprintf(stderr, "filelist_tree_add_item: path '%s' is not a valid path\n", path); return FALSE; } // remove leading './' if (strstr(path, "./") == path) path += 2; // remove leading and trailing '/' if present char *p; if (IS_DIR_SEP(*path)) p = exclude_trailing_path_sep(path + 1); else p = exclude_trailing_path_sep(path); log(" filelist_tree_add_item: p = '%s'\n", p); char *pp; pp = canonicalize_filename (p); if (! pp) pp = strdup (p); struct PathTree* 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->index = index; // free old data if (found->data) free_vfs_item(found->data); found->data = item; if (found->data) found->data->FName = strdup(found->node); if (found->data) found->data->FDisplayName = strdup(found->node); } else // create new item recursively filelist_tree_add_item_recurr(tree, pp, original_pathstr, item, index); free(p); free(pp); return TRUE; } struct PathTree* filelist_tree_get_item_by_index(struct PathTree *tree, unsigned long index) { struct PathTree* t = NULL; if ((tree) && (tree->items) && (tree->items->len > index)) { t = (struct PathTree*)g_ptr_array_index(tree->items, index); } return t; } void filelist_tree_resolve_symlinks_recurr(struct PathTree *tree, struct PathTree *root_tree, const char *path) { if (tree) { if ((tree->items) && (tree->items->len > 0)) { unsigned int i; for (i = 0; i < tree->items->len; i++) { struct PathTree* t = (struct PathTree*)g_ptr_array_index(tree->items, i); gchar *new_path; if ((t) && (t->data) && (t->data->ItemType == vSymlink) && (t->data->sLinkTo)) { log("filelist_tree_resolve_symlinks: found '%s/%s' --> '%s'\n", path, t->node, t->data->sLinkTo); gchar *rel = resolve_relative(path, t->data->sLinkTo); log(" filelist_tree_resolve_symlinks: relative = '%s'\n", rel); if (rel) { struct PathTree* link_t = filelist_tree_find_node_by_path(root_tree, rel); if ((link_t) && (link_t->data)) { /* WARNING: this is dangerous since we're destroying the symlink type */ // t->data->ItemType = link_t->data->ItemType; t->data->a_time = link_t->data->a_time; t->data->c_time = link_t->data->c_time; t->data->m_time = link_t->data->m_time; t->data->iGID = link_t->data->iGID; t->data->iUID = link_t->data->iUID; t->data->iMode = link_t->data->iMode; t->data->iSize = link_t->data->iSize; } g_free(rel); } } if ((strlen(path) == 1) && (IS_DIR_SEP(*path))) new_path = g_strconcat("/", t->node, NULL); else new_path = g_strconcat(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"); }