/*
* Unix SMB/CIFS implementation.
* Generic Abstract Data Types
* Copyright (C) Gerald Carter 2002.
*
* 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 3 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, see .
*/
#include "includes.h"
#include "adt_tree.h"
struct tree_node {
struct tree_node *parent;
struct tree_node **children;
int num_children;
char *key;
void *data_p;
};
struct sorted_tree {
struct tree_node *root;
};
/**************************************************************************
*************************************************************************/
static bool trim_tree_keypath( char *path, char **base, char **new_path )
{
char *p;
*new_path = *base = NULL;
if ( !path )
return False;
*base = path;
p = strchr( path, '\\' );
if ( p ) {
*p = '\0';
*new_path = p+1;
}
return True;
}
/**************************************************************************
Initialize the tree's root.
*************************************************************************/
struct sorted_tree *pathtree_init(void *data_p)
{
struct sorted_tree *tree = NULL;
tree = talloc_zero(NULL, struct sorted_tree);
if (tree == NULL) {
return NULL;
}
tree->root = talloc_zero(tree, struct tree_node);
if (tree->root == NULL) {
TALLOC_FREE( tree );
return NULL;
}
tree->root->data_p = data_p;
return tree;
}
/**************************************************************************
Find the next child given a key string
*************************************************************************/
static struct tree_node *pathtree_birth_child(struct tree_node *node,
char* key )
{
struct tree_node *infant = NULL;
struct tree_node **siblings;
int i;
infant = talloc_zero(node, struct tree_node);
if (infant == NULL) {
return NULL;
}
infant->key = talloc_strdup( infant, key );
infant->parent = node;
siblings = talloc_realloc(node, node->children, struct tree_node *,
node->num_children+1);
if ( siblings )
node->children = siblings;
node->num_children++;
/* first child */
if ( node->num_children == 1 ) {
DEBUG(11,("pathtree_birth_child: First child of node [%s]! [%s]\n",
node->key ? node->key : "NULL", infant->key ));
node->children[0] = infant;
}
else
{
/*
* multiple siblings .... (at least 2 children)
*
* work from the end of the list forward
* The last child is not set at this point
* Insert the new infanct in ascending order
* from left to right
*/
for ( i = node->num_children-1; i>=1; i-- )
{
DEBUG(11,("pathtree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n",
infant->key, node->children[i-1]->key));
/* the strings should never match assuming that we
have called pathtree_find_child() first */
if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) {
DEBUG(11,("pathtree_birth_child: storing infant in i == [%d]\n",
i));
node->children[i] = infant;
break;
}
/* bump everything towards the end on slot */
node->children[i] = node->children[i-1];
}
DEBUG(11,("pathtree_birth_child: Exiting loop (i == [%d])\n", i ));
/* if we haven't found the correct slot yet, the child
will be first in the list */
if ( i == 0 )
node->children[0] = infant;
}
return infant;
}
/**************************************************************************
Find the next child given a key string
*************************************************************************/
static struct tree_node *pathtree_find_child(struct tree_node *node,
char *key )
{
struct tree_node *next = NULL;
int i, result;
if ( !node ) {
DEBUG(0,("pathtree_find_child: NULL node passed into function!\n"));
return NULL;
}
if ( !key ) {
DEBUG(0,("pathtree_find_child: NULL key string passed into function!\n"));
return NULL;
}
for ( i=0; inum_children; i++ )
{
DEBUG(11,("pathtree_find_child: child key => [%s]\n",
node->children[i]->key));
result = StrCaseCmp( node->children[i]->key, key );
if ( result == 0 )
next = node->children[i];
/* if result > 0 then we've gone to far because
the list of children is sorted by key name
If result == 0, then we have a match */
if ( result > 0 )
break;
}
DEBUG(11,("pathtree_find_child: %s [%s]\n",
next ? "Found" : "Did not find", key ));
return next;
}
/**************************************************************************
Add a new node into the tree given a key path and a blob of data
*************************************************************************/
WERROR pathtree_add(struct sorted_tree *tree, const char *path, void *data_p)
{
char *str, *base, *path2;
struct tree_node *current, *next;
WERROR ret = WERR_OK;
DEBUG(8,("pathtree_add: Enter\n"));
if ( !path || *path != '\\' ) {
DEBUG(0,("pathtree_add: Attempt to add a node with a bad path [%s]\n",
path ? path : "NULL" ));
return WERR_INVALID_PARAM;
}
if ( !tree ) {
DEBUG(0,("pathtree_add: Attempt to add a node to an uninitialized tree!\n"));
return WERR_INVALID_PARAM;
}
/* move past the first '\\' */
path++;
path2 = SMB_STRDUP( path );
if ( !path2 ) {
DEBUG(0,("pathtree_add: strdup() failed on string [%s]!?!?!\n", path));
return WERR_NOMEM;
}
/*
* this works sort of like a 'mkdir -p' call, possibly
* creating an entire path to the new node at once
* The path should be of the form ///...
*/
base = path2;
str = path2;
current = tree->root;
do {
/* break off the remaining part of the path */
str = strchr( str, '\\' );
if ( str )
*str = '\0';
/* iterate to the next child--birth it if necessary */
next = pathtree_find_child( current, base );
if ( !next ) {
next = pathtree_birth_child( current, base );
if ( !next ) {
DEBUG(0,("pathtree_add: Failed to create new child!\n"));
ret = WERR_NOMEM;
goto done;
}
}
current = next;
/* setup the next part of the path */
base = str;
if ( base ) {
*base = '\\';
base++;
str = base;
}
} while ( base != NULL );
current->data_p = data_p;
DEBUG(10,("pathtree_add: Successfully added node [%s] to tree\n",
path ));
DEBUG(8,("pathtree_add: Exit\n"));
done:
SAFE_FREE( path2 );
return ret;
}
/**************************************************************************
Recursive routine to print out all children of a struct tree_node
*************************************************************************/
static void pathtree_print_children(TALLOC_CTX *ctx,
struct tree_node *node,
int debug,
const char *path )
{
int i;
int num_children;
char *path2 = NULL;
if ( !node )
return;
if ( node->key )
DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key,
node->data_p ? "data" : "NULL" ));
if ( path ) {
path2 = talloc_strdup(ctx, path);
if (!path2) {
return;
}
}
path2 = talloc_asprintf(ctx,
"%s%s/",
path ? path : "",
node->key ? node->key : "NULL");
if (!path2) {
return;
}
num_children = node->num_children;
for ( i=0; ichildren[i], debug, path2 );
}
}
/**************************************************************************
Dump the kys for a tree to the log file
*************************************************************************/
void pathtree_print_keys(struct sorted_tree *tree, int debug )
{
int i;
int num_children = tree->root->num_children;
if ( tree->root->key )
DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key,
tree->root->data_p ? "data" : "NULL" ));
for ( i=0; iroot->children[i], debug,
tree->root->key ? tree->root->key : "ROOT/" );
TALLOC_FREE(ctx);
}
}
/**************************************************************************
return the data_p for for the node in tree matching the key string
The key string is the full path. We must break it apart and walk
the tree
*************************************************************************/
void* pathtree_find(struct sorted_tree *tree, char *key )
{
char *keystr, *base = NULL, *str = NULL, *p;
struct tree_node *current;
void *result = NULL;
DEBUG(10,("pathtree_find: Enter [%s]\n", key ? key : "NULL" ));
/* sanity checks first */
if ( !key ) {
DEBUG(0,("pathtree_find: Attempt to search tree using NULL search string!\n"));
return NULL;
}
if ( !tree ) {
DEBUG(0,("pathtree_find: Attempt to search an uninitialized tree using string [%s]!\n",
key ? key : "NULL" ));
return NULL;
}
if ( !tree->root )
return NULL;
/* make a copy to play with */
if ( *key == '\\' )
keystr = SMB_STRDUP( key+1 );
else
keystr = SMB_STRDUP( key );
if ( !keystr ) {
DEBUG(0,("pathtree_find: strdup() failed on string [%s]!?!?!\n", key));
return NULL;
}
/* start breaking the path apart */
p = keystr;
current = tree->root;
if ( tree->root->data_p )
result = tree->root->data_p;
do
{
/* break off the remaining part of the path */
trim_tree_keypath( p, &base, &str );
DEBUG(11,("pathtree_find: [loop] base => [%s], new_path => [%s]\n",
base ? base : "",
str ? str : ""));
/* iterate to the next child */
current = pathtree_find_child( current, base );
/*
* the idea is that the data_p for a parent should
* be inherited by all children, but allow it to be
* overridden farther down
*/
if ( current && current->data_p )
result = current->data_p;
/* reset the path pointer 'p' to the remaining part of the key string */
p = str;
} while ( str && current );
/* result should be the data_p from the lowest match node in the tree */
if ( result )
DEBUG(11,("pathtree_find: Found data_p!\n"));
SAFE_FREE( keystr );
DEBUG(10,("pathtree_find: Exit\n"));
return result;
}