2002-07-19 03:00:24 +04:00
/*
* 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
2007-07-09 23:25:36 +04:00
* the Free Software Foundation ; either version 3 of the License , or
2002-07-19 03:00:24 +04:00
* ( 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
2007-07-10 09:23:25 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2002-07-19 03:00:24 +04:00
*/
# include "includes.h"
2005-02-23 19:36:44 +03:00
# include "adt_tree.h"
2002-07-19 03:00:24 +04:00
2002-07-20 06:42:04 +04:00
/**************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
static bool trim_tree_keypath ( char * path , char * * base , char * * new_path )
2002-07-20 06:42:04 +04:00
{
char * p ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
* new_path = * base = NULL ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
if ( ! path )
return False ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
* base = path ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
p = strchr ( path , ' / ' ) ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
if ( p ) {
* p = ' \0 ' ;
* new_path = p + 1 ;
}
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
return True ;
}
2002-07-19 03:00:24 +04:00
/**************************************************************************
Initialize the tree ' s root . The cmp_fn is a callback function used
for comparision of two children
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-03 20:38:51 +04:00
SORTED_TREE * pathtree_init ( void * data_p , int ( cmp_fn ) ( void * , void * ) )
2002-07-19 03:00:24 +04:00
{
SORTED_TREE * tree = NULL ;
2010-02-07 17:01:57 +03:00
2005-09-03 20:38:51 +04:00
if ( ! ( tree = TALLOC_ZERO_P ( NULL , SORTED_TREE ) ) )
2002-07-19 03:00:24 +04:00
return NULL ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
tree - > compare = cmp_fn ;
2010-02-07 17:01:57 +03:00
2005-09-03 20:38:51 +04:00
if ( ! ( tree - > root = TALLOC_ZERO_P ( tree , TREE_NODE ) ) ) {
TALLOC_FREE ( tree ) ;
2002-07-19 03:00:24 +04:00
return NULL ;
}
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
tree - > root - > data_p = data_p ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
return tree ;
}
/**************************************************************************
Find the next child given a key string
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-02-23 19:36:44 +03:00
static TREE_NODE * pathtree_birth_child ( TREE_NODE * node , char * key )
2002-07-19 03:00:24 +04:00
{
TREE_NODE * infant = NULL ;
TREE_NODE * * siblings ;
2002-07-20 06:42:04 +04:00
int i ;
2010-02-07 17:01:57 +03:00
2005-09-03 20:38:51 +04:00
if ( ! ( infant = TALLOC_ZERO_P ( node , TREE_NODE ) ) )
2002-07-19 03:00:24 +04:00
return NULL ;
2010-02-07 17:01:57 +03:00
2005-09-03 20:38:51 +04:00
infant - > key = talloc_strdup ( infant , key ) ;
2002-07-19 03:00:24 +04:00
infant - > parent = node ;
2010-02-07 17:01:57 +03:00
2005-09-03 20:38:51 +04:00
siblings = TALLOC_REALLOC_ARRAY ( node , node - > children , TREE_NODE * , node - > num_children + 1 ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( siblings )
node - > children = siblings ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
node - > num_children + + ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* first child */
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( node - > num_children = = 1 ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_birth_child: First child of node [%s]! [%s] \n " ,
2002-07-19 03:00:24 +04:00
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
*/
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
for ( i = node - > num_children - 1 ; i > = 1 ; i - - )
{
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_birth_child: Looking for crib; infant -> [%s], child -> [%s] \n " ,
2002-07-19 22:49:44 +04:00
infant - > key , node - > children [ i - 1 ] - > key ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* the strings should never match assuming that we
2005-02-23 19:36:44 +03:00
have called pathtree_find_child ( ) first */
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
if ( StrCaseCmp ( infant - > key , node - > children [ i - 1 ] - > key ) > 0 ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_birth_child: storing infant in i == [%d] \n " ,
2002-07-20 06:42:04 +04:00
i ) ) ;
2002-07-19 22:49:44 +04:00
node - > children [ i ] = infant ;
2002-07-19 03:00:24 +04:00
break ;
}
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
/* bump everything towards the end on slot */
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
node - > children [ i ] = node - > children [ i - 1 ] ;
2002-07-19 03:00:24 +04:00
}
2002-07-20 06:42:04 +04:00
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_birth_child: Exiting loop (i == [%d]) \n " , i ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
/* if we haven't found the correct slot yet, the child
2002-07-19 22:49:44 +04:00
will be first in the list */
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
if ( i = = 0 )
node - > children [ 0 ] = infant ;
2002-07-19 03:00:24 +04:00
}
return infant ;
}
2002-07-20 06:42:04 +04:00
2002-07-19 03:00:24 +04:00
/**************************************************************************
Find the next child given a key string
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-02-23 19:36:44 +03:00
static TREE_NODE * pathtree_find_child ( TREE_NODE * node , char * key )
2002-07-19 03:00:24 +04:00
{
TREE_NODE * next = NULL ;
int i , result ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! node ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_find_child: NULL node passed into function! \n " ) ) ;
2002-07-19 03:00:24 +04:00
return NULL ;
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! key ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_find_child: NULL key string passed into function! \n " ) ) ;
2002-07-19 03:00:24 +04:00
return NULL ;
}
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
for ( i = 0 ; i < node - > num_children ; i + + )
{
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_find_child: child key => [%s] \n " ,
2002-07-20 06:42:04 +04:00
node - > children [ i ] - > key ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
result = StrCaseCmp ( node - > children [ i ] - > key , key ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( result = = 0 )
next = node - > children [ i ] ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* 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 */
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
if ( result > 0 )
2002-07-19 03:00:24 +04:00
break ;
}
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_find_child: %s [%s] \n " ,
2002-07-20 06:42:04 +04:00
next ? " Found " : " Did not find " , key ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
return next ;
}
/**************************************************************************
Add a new node into the tree given a key path and a blob of data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-04-13 16:41:44 +04:00
WERROR pathtree_add ( SORTED_TREE * tree , const char * path , void * data_p )
2002-07-19 03:00:24 +04:00
{
char * str , * base , * path2 ;
TREE_NODE * current , * next ;
2008-04-13 16:41:44 +04:00
WERROR ret = WERR_OK ;
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
DEBUG ( 8 , ( " pathtree_add: Enter \n " ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! path | | * path ! = ' / ' ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_add: Attempt to add a node with a bad path [%s] \n " ,
2002-07-19 03:00:24 +04:00
path ? path : " NULL " ) ) ;
2008-04-13 16:41:44 +04:00
return WERR_INVALID_PARAM ;
2002-07-19 03:00:24 +04:00
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! tree ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_add: Attempt to add a node to an uninitialized tree! \n " ) ) ;
2008-04-13 16:41:44 +04:00
return WERR_INVALID_PARAM ;
2002-07-19 03:00:24 +04:00
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* move past the first '/' */
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
path + + ;
2004-12-07 21:25:53 +03:00
path2 = SMB_STRDUP ( path ) ;
2002-07-19 03:00:24 +04:00
if ( ! path2 ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_add: strdup() failed on string [%s]!?!?! \n " , path ) ) ;
2008-04-13 16:41:44 +04:00
return WERR_NOMEM ;
2002-07-19 03:00:24 +04:00
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/*
* 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 / < key1 > / < key2 > / . . .
*/
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
base = path2 ;
str = path2 ;
current = tree - > root ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
do {
/* break off the remaining part of the path */
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
str = strchr ( str , ' / ' ) ;
if ( str )
* str = ' \0 ' ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* iterate to the next child--birth it if necessary */
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
next = pathtree_find_child ( current , base ) ;
2002-07-19 03:00:24 +04:00
if ( ! next ) {
2005-02-23 19:36:44 +03:00
next = pathtree_birth_child ( current , base ) ;
2002-07-19 03:00:24 +04:00
if ( ! next ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_add: Failed to create new child! \n " ) ) ;
2008-04-13 16:41:44 +04:00
ret = WERR_NOMEM ;
2002-07-19 03:00:24 +04:00
goto done ;
}
}
current = next ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* setup the next part of the path */
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
base = str ;
if ( base ) {
* base = ' / ' ;
base + + ;
str = base ;
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
} while ( base ! = NULL ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
current - > data_p = data_p ;
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
DEBUG ( 10 , ( " pathtree_add: Successfully added node [%s] to tree \n " ,
2002-07-19 03:00:24 +04:00
path ) ) ;
2005-02-23 19:36:44 +03:00
DEBUG ( 8 , ( " pathtree_add: Exit \n " ) ) ;
2002-07-19 03:00:24 +04:00
done :
SAFE_FREE ( path2 ) ;
return ret ;
}
/**************************************************************************
Recursive routine to print out all children of a TREE_NODE
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-09-15 02:14:39 +04:00
static void pathtree_print_children ( TALLOC_CTX * ctx ,
TREE_NODE * node ,
int debug ,
const char * path )
2002-07-19 03:00:24 +04:00
{
int i ;
int num_children ;
2007-09-15 02:14:39 +04:00
char * path2 = NULL ;
2002-07-19 03:00:24 +04:00
if ( ! node )
return ;
2007-09-15 02:14:39 +04:00
2002-07-19 03:00:24 +04:00
if ( node - > key )
DEBUG ( debug , ( " %s: [%s] (%s) \n " , path ? path : " NULL " , node - > key ,
node - > data_p ? " data " : " NULL " ) ) ;
2007-09-15 02:14:39 +04:00
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 ;
}
2002-07-19 03:00:24 +04:00
2007-09-15 02:14:39 +04:00
num_children = node - > num_children ;
for ( i = 0 ; i < num_children ; i + + ) {
pathtree_print_children ( ctx , node - > children [ i ] , debug , path2 ) ;
}
2002-07-19 03:00:24 +04:00
}
/**************************************************************************
Dump the kys for a tree to the log file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-02-23 19:36:44 +03:00
void pathtree_print_keys ( SORTED_TREE * tree , int debug )
2002-07-19 03:00:24 +04:00
{
int i ;
int num_children = tree - > root - > num_children ;
2007-09-15 02:14:39 +04:00
2002-07-19 03:00:24 +04:00
if ( tree - > root - > key )
DEBUG ( debug , ( " ROOT/: [%s] (%s) \n " , tree - > root - > key ,
tree - > root - > data_p ? " data " : " NULL " ) ) ;
2007-09-15 02:14:39 +04:00
2002-07-19 03:00:24 +04:00
for ( i = 0 ; i < num_children ; i + + ) {
2007-09-15 02:14:39 +04:00
TALLOC_CTX * ctx = talloc_stackframe ( ) ;
pathtree_print_children ( ctx , tree - > root - > children [ i ] , debug ,
2002-07-19 03:00:24 +04:00
tree - > root - > key ? tree - > root - > key : " ROOT/ " ) ;
2007-09-15 02:14:39 +04:00
TALLOC_FREE ( ctx ) ;
2002-07-19 03:00:24 +04:00
}
2007-09-15 02:14:39 +04:00
2002-07-19 03:00:24 +04:00
}
/**************************************************************************
return the data_p for for the node in tree matching the key string
2007-09-15 02:14:39 +04:00
The key string is the full path . We must break it apart and walk
2002-07-19 03:00:24 +04:00
the tree
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-02-23 19:36:44 +03:00
void * pathtree_find ( SORTED_TREE * tree , char * key )
2002-07-19 03:00:24 +04:00
{
2007-01-16 21:05:37 +03:00
char * keystr , * base = NULL , * str = NULL , * p ;
2002-07-19 03:00:24 +04:00
TREE_NODE * current ;
void * result = NULL ;
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
DEBUG ( 10 , ( " pathtree_find: Enter [%s] \n " , key ? key : " NULL " ) ) ;
2002-07-19 03:00:24 +04:00
/* sanity checks first */
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! key ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_find: Attempt to search tree using NULL search string! \n " ) ) ;
2002-07-19 03:00:24 +04:00
return NULL ;
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! tree ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_find: Attempt to search an uninitialized tree using string [%s]! \n " ,
2002-07-19 03:00:24 +04:00
key ? key : " NULL " ) ) ;
return NULL ;
}
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! tree - > root )
return NULL ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
/* make a copy to play with */
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
if ( * key = = ' / ' )
2004-12-07 21:25:53 +03:00
keystr = SMB_STRDUP ( key + 1 ) ;
2002-07-20 06:42:04 +04:00
else
2004-12-07 21:25:53 +03:00
keystr = SMB_STRDUP ( key ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
if ( ! keystr ) {
2005-02-23 19:36:44 +03:00
DEBUG ( 0 , ( " pathtree_find: strdup() failed on string [%s]!?!?! \n " , key ) ) ;
2002-07-19 03:00:24 +04:00
return NULL ;
}
/* start breaking the path apart */
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
p = keystr ;
2002-07-19 03:00:24 +04:00
current = tree - > root ;
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
if ( tree - > root - > data_p )
result = tree - > root - > data_p ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
do
{
/* break off the remaining part of the path */
2002-07-20 06:42:04 +04:00
trim_tree_keypath ( p , & base , & str ) ;
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_find: [loop] base => [%s], new_path => [%s] \n " ,
2007-01-16 21:05:37 +03:00
base ? base : " " ,
str ? str : " " ) ) ;
2002-07-19 03:00:24 +04:00
/* iterate to the next child */
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
current = pathtree_find_child ( current , base ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
/*
* the idea is that the data_p for a parent should
* be inherited by all children , but allow it to be
* overridden farther down
*/
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
if ( current & & current - > data_p )
result = current - > data_p ;
2002-07-20 06:42:04 +04:00
/* reset the path pointer 'p' to the remaining part of the key string */
p = str ;
2010-02-07 17:01:57 +03:00
2002-07-20 06:42:04 +04:00
} while ( str & & current ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 22:49:44 +04:00
/* result should be the data_p from the lowest match node in the tree */
2002-07-20 02:16:03 +04:00
if ( result )
2005-02-23 19:36:44 +03:00
DEBUG ( 11 , ( " pathtree_find: Found data_p! \n " ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
SAFE_FREE ( keystr ) ;
2010-02-07 17:01:57 +03:00
2005-02-23 19:36:44 +03:00
DEBUG ( 10 , ( " pathtree_find: Exit \n " ) ) ;
2010-02-07 17:01:57 +03:00
2002-07-19 03:00:24 +04:00
return result ;
}