blob: 8f546a9de902244657c06244688735c705bf29c7 [file] [log] [blame]
/*******************************************************************************
* 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;
}