| /******************************************************************************* |
| * Copyright (C) 2010, Linaro Limited. |
| * |
| * This file is part of PowerDebug. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Author: |
| * Daniel Lezcano <daniel.lezcano@linaro.org> |
| * |
| *******************************************************************************/ |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #undef _GNU_SOURCE |
| #include <stdlib.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "tree.h" |
| |
| /* |
| * Allocate a tree structure and initialize the different fields. |
| * |
| * @path : the absolute path to the directory |
| * @depth : the depth in the tree |
| * Returns a tree structure on success, NULL otherwise |
| */ |
| static inline struct tree *tree_alloc(const char *path, int depth) |
| { |
| struct tree *t; |
| |
| t = malloc(sizeof(*t)); |
| if (!t) |
| return NULL; |
| |
| /* Full pathname */ |
| t->path = strdup(path); |
| if (!t->path) { |
| free(t); |
| return NULL; |
| } |
| |
| /* Basename pointer on the full path name */ |
| t->name = strrchr(t->path, '/') + 1; |
| |
| t->depth = depth; |
| t->tail = t; |
| t->child = NULL; |
| t->parent = NULL; |
| t->next = NULL; |
| t->prev = NULL; |
| t->private = NULL; |
| |
| return t; |
| } |
| |
| /* |
| * Free a tree structure and the fields we allocated in the |
| * tree_alloc function. |
| * |
| * @t : the tree structure to be freed |
| */ |
| static inline void tree_free(struct tree *t) |
| { |
| free(t->path); |
| free(t); |
| } |
| |
| /* |
| * Add at the end of the list the new list element. |
| * |
| * @head : the list to be appened |
| * @new : the new element to be added at the end of the list |
| */ |
| static inline void tree_add_tail(struct tree *head, struct tree *new) |
| { |
| new->prev = head->tail; |
| head->tail->next = new; |
| head->tail = new; |
| } |
| |
| /* |
| * Add a child in to a parent list, at the end of this list. |
| * |
| * @parent : the parent list to add the child |
| * @child : the child to be added |
| */ |
| static inline void tree_add_child(struct tree *parent, struct tree *child) |
| { |
| child->parent = parent; |
| |
| if (parent->child) |
| return tree_add_tail(parent->child, child); |
| |
| parent->child = child; |
| } |
| |
| /* |
| * This function will browse the directory structure and build a |
| * tree reflecting the content of the directory tree. |
| * |
| * @tree : the root node of the tree |
| * @filter : a callback to filter out the directories |
| * Returns 0 on success, -1 otherwise |
| */ |
| static int tree_scan(struct tree *tree, tree_filter_t filter) |
| { |
| DIR *dir; |
| char *basedir, *newpath; |
| struct dirent dirent, *direntp; |
| struct stat s; |
| int ret = 0; |
| |
| dir = opendir(tree->path); |
| if (!dir) |
| return -1; |
| |
| while (!readdir_r(dir, &dirent, &direntp)) { |
| |
| struct tree *child; |
| |
| if (!direntp) |
| break; |
| |
| if (direntp->d_name[0] == '.') |
| continue; |
| |
| if (filter && filter(direntp->d_name)) |
| continue; |
| |
| ret = asprintf(&basedir, "%s", tree->path); |
| if (ret < 0) |
| return -1; |
| |
| ret = basename(basedir) ? 0 : -1; |
| if (ret < 0) |
| goto out_free_basedir; |
| |
| ret = asprintf(&newpath, "%s/%s", basedir, direntp->d_name); |
| if (ret < 0) |
| goto out_free_basedir; |
| |
| ret = stat(newpath, &s); |
| if (ret) |
| goto out_free_newpath; |
| |
| if (S_ISDIR(s.st_mode)) { |
| |
| ret = -1; |
| |
| child = tree_alloc(newpath, tree->depth + 1); |
| if (!child) |
| goto out_free_newpath; |
| |
| tree_add_child(tree, child); |
| |
| ret = tree_scan(child, filter); |
| } |
| |
| out_free_newpath: |
| free(newpath); |
| |
| out_free_basedir: |
| free(basedir); |
| |
| if (ret) |
| break; |
| } |
| |
| closedir(dir); |
| |
| return ret; |
| } |
| |
| /* |
| * This function takes the topmost directory path and populate the |
| * directory tree structures. |
| * |
| * @tree : a path to the topmost directory path |
| * Returns a tree structure corresponding to the root node of the |
| * directory tree representation on success, NULL otherwise |
| */ |
| struct tree *tree_load(const char *path, tree_filter_t filter) |
| { |
| struct tree *tree; |
| |
| tree = tree_alloc(path, 0); |
| if (!tree) |
| return NULL; |
| |
| if (tree_scan(tree, filter)) { |
| tree_free(tree); |
| return NULL; |
| } |
| |
| return tree; |
| } |
| |
| /* |
| * This function will go over the tree passed as parameter and |
| * will call the callback passed as parameter for each node. |
| * |
| * @tree : the topmost node where we begin to browse the tree |
| * Returns 0 on success, < 0 otherwise |
| */ |
| int tree_for_each(struct tree *tree, tree_cb_t cb, void *data) |
| { |
| if (!tree) |
| return 0; |
| |
| if (cb(tree, data)) |
| return -1; |
| |
| if (tree_for_each(tree->child, cb, data)) |
| return -1; |
| |
| return tree_for_each(tree->next, cb, data); |
| } |
| |
| int tree_for_each_reverse(struct tree *tree, tree_cb_t cb, void *data) |
| { |
| if (!tree) |
| return 0; |
| |
| if (cb(tree, data)) |
| return -1; |
| |
| if (tree_for_each_reverse(tree->prev, cb, data)) |
| return -1; |
| |
| return tree_for_each_reverse(tree->parent, cb, data); |
| } |
| |
| int tree_for_each_parent(struct tree *tree, tree_cb_t cb, void *data) |
| { |
| if (!tree) |
| return 0; |
| |
| if (tree_for_each_parent(tree->parent, cb, data)) |
| return -1; |
| |
| return cb(tree, data); |
| } |
| |
| struct tree *tree_find(struct tree *tree, const char *name) |
| { |
| struct tree *t; |
| |
| if (!tree) |
| return NULL; |
| |
| if (!strcmp(tree->name, name)) |
| return tree; |
| |
| t = tree_find(tree->child, name); |
| if (t) |
| return t; |
| |
| return tree_find(tree->next, name); |
| } |
| |
| struct struct_find { |
| int nr; |
| const char *name; |
| struct tree ***ptree; |
| }; |
| |
| static int tree_finds_cb(struct tree *tree, void *data) |
| { |
| struct struct_find *sf = data; |
| |
| if (strncmp(sf->name, tree->name, strlen(sf->name))) |
| return 0; |
| |
| if (sf->ptree) |
| (*(sf->ptree))[sf->nr] = tree; |
| |
| sf->nr++; |
| |
| return 0; |
| } |
| |
| int tree_finds(struct tree *tree, const char *name, struct tree ***ptr) |
| { |
| struct struct_find sf = { .nr = 0, .ptree = NULL, .name = name }; |
| int nmatch; |
| |
| /* first pass : count # of matching nodes */ |
| tree_for_each(tree, tree_finds_cb, &sf); |
| |
| /* no match */ |
| if (!sf.nr) |
| return 0; |
| |
| *ptr = malloc(sizeof(struct tree *) * sf.nr); |
| if (!*ptr) |
| return -1; |
| |
| /* store the result as it will be overwritten by the next call */ |
| nmatch = sf.nr; |
| sf.nr = 0; |
| sf.ptree = ptr; |
| |
| /* second pass : fill with the matching nodes */ |
| tree_for_each(tree, tree_finds_cb, &sf); |
| |
| return nmatch; |
| } |