/*
 pbl.c - basic library functions

 Copyright (C) 2002    Peter Graf

   This file is part of PBL - The Program Base Library.
   PBL is free software.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   For more information on the Program Base Library or Peter Graf,
   please see: http://mission.base.com/.

   $Log: pbl.cpp,v $
   Revision 1.2  2003/08/02 19:04:06  meilof
   some build fixes

   Revision 1.1  2003/03/21 22:48:55  meilof
    * finally a good hashvector implementation
    * masterfile fixes

   Revision 1.2  2002/09/12 20:47:13  peter
   added the isam file handling to the library


*/
/*
 * make sure "strings <exe> | grep Id | sort -u" shows the source file versions
 */
static char* _PBL_id = "$Id: pbl.cpp,v 1.2 2003/08/02 19:04:06 meilof Exp $";
static int   _PBL_fct() { return( _PBL_id ? 0 : _PBL_fct() ); }

/*
 * make sure "strings <exe> | grep Id | sort -u" shows the source file versions
 */
static char* rcsid = "$Id: pbl.cpp,v 1.2 2003/08/02 19:04:06 meilof Exp $";
static int   rcsid_fct() { return( rcsid ? 0 : rcsid_fct() ); }


#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <stdlib.h>
#include <time.h>

#include "pbl.h"

/*****************************************************************************/
/* #defines                                                                  */
/*****************************************************************************/

/*****************************************************************************/
/* typedefs                                                                  */
/*****************************************************************************/

/*
 * the type is only needed if we keep a heap memory trace
 */
typedef struct pbl_memtrace_s
{
    char *      tag;          /* tag used by calling function                */
    time_t      time;         /* time when the chunk of memory was requested */
    void *      data;         /* pointer to data that was allocated          */
    size_t      size;         /* number of bytes allocated                   */

    struct pbl_memtrace_s * next;    /* memory chunks are kept in a linear   */
    struct pbl_memtrace_s * prev;    /* list                                 */

} pbl_memtrace_t;

/*****************************************************************************/
/* globals                                                                   */
/*****************************************************************************/

#ifdef PBL_MEMTRACE

/*
 * head and tail of known memory chunks
 */
static pbl_memtrace_t * pbl_memtrace_head;
static pbl_memtrace_t * pbl_memtrace_tail;

/*
 * number of memory chunks known
 */
static long pbl_nmem_chunks = 0;

/*
 * total size of all chunks known
 */
static long pbl_nmem_size   = 0;

#endif

static char pbl_errbuf[ PBL_ERRSTR_LEN + 1 ];

int    pbl_errno;
char * pbl_errstr = pbl_errbuf;

/*****************************************************************************/
/* functions                                                                 */
/*****************************************************************************/

#ifdef PBL_MEMTRACE

/*
 * log a line for all memory chunks that are allocated for more than
 * 3 minutes, or if call at the end of the program, log all chunks known
 */
void pbl_memtrace_out( int checktime )
{
    static int first = 1;
    pbl_memtrace_t * memtrace;
    pbl_memtrace_t * tmp;

    char * outpath = "pblmemtrace.log";
    FILE * outfile = NULL;
    time_t now = time( 0 );
    char * nowstr = NULL;
    char * timestr = NULL;

    if( !pbl_memtrace_head )
    {
        return;
    }

    memtrace = pbl_memtrace_head;
    while( memtrace )
    {
        if( checktime && ( now - memtrace->time < 180 ))
        {
            break;
        }

        if( !outfile )
        {
            if( first )
            {
                first = 0;
                outfile = fopen( outpath, "w" );
                if( outfile )
                {
                    fprintf( outfile, ">>memtrace at %s", ctime( &now ));
                }
            }
            else
            {
                outfile = fopen( outpath, "a" );
            }

            if( !outfile )
            {
                break;
            }
        }

        tmp = memtrace;
        memtrace = memtrace->next;

        if( !nowstr )
        {
            nowstr = strdup( ctime( &now ));
        }
        timestr = ctime( &(tmp->time));

        fprintf( outfile, "%s %.*s: %.*s %06ld %x %ld %ld \"%s\"\n",
                          checktime ? ">" : "e",
                          8, nowstr ? nowstr + 11 : "unknown",
                          8, timestr ? timestr + 11 : "unknown",
                          (long)tmp->size,
                          tmp->data - NULL,
                          pbl_nmem_chunks, pbl_nmem_size, tmp->tag
               );

        PBL_LIST_UNLINK( pbl_memtrace_head, pbl_memtrace_tail,
                         tmp, next, prev );
        free( tmp );
    }

    if( nowstr )
    {
        free( nowstr );
    }

    if( outfile )
    {
        fclose( outfile );
    }
}

/*
 * remember a memory chunk that was allocated by some function
 */
void pbl_memtrace_create( 
char * tag,
void * data,
size_t size
)
{
    pbl_memtrace_t * memtrace;

    memtrace = malloc( sizeof( pbl_memtrace_t ));
    if( !memtrace )
    {
        return;
    }

    memtrace->tag  = tag;
    memtrace->time = time( 0 );
    memtrace->data = data;
    memtrace->size = size;

    PBL_LIST_APPEND( pbl_memtrace_head, pbl_memtrace_tail,
                     memtrace, next, prev );

    pbl_nmem_chunks++;
    pbl_nmem_size += size;

    pbl_memtrace_out( 1 );
}

/*
 * remove a memory from the chunk list, the caller freed the memory
 */
void pbl_memtrace_delete(
void * data
)
{
    pbl_memtrace_t * memtrace;
    
    for( memtrace = pbl_memtrace_head; memtrace; memtrace = memtrace->next )
    {
        if( memtrace->data == data )
        {
            pbl_nmem_chunks--;
            pbl_nmem_size -= memtrace->size;

            PBL_LIST_UNLINK( pbl_memtrace_head, pbl_memtrace_tail,
                             memtrace, next, prev );
            free( memtrace );
            break;
        }
    }
}

#endif /* PBL_MEMTRACE */

/**
  * replacement for malloc
  * 
  * @return  void * retptr == NULL: OUT OF MEMORY
  * @return  void * retptr != NULL: pointer to buffer allocated
  */
void * pbl_malloc(
char   * tag,        /** tag used for memory leak detection */
size_t   size        /** number of bytes to allocate        */
)
{
    void * ptr;

    if( !tag )
    {
        tag = "pbl_malloc";
    }

    ptr = malloc( size );
    if( !ptr )
    {
        snprintf( pbl_errstr, PBL_ERRSTR_LEN,
                  "%s: failed to malloc %d bytes\n", tag, size );
        pbl_errno = PBL_ERROR_OUT_OF_MEMORY;
        return( 0 );
    }

#ifdef PBL_MEMTRACE
    pbl_memtrace_create( tag, ptr, size );
#endif
    
    return( ptr );
}

/**
  * replacement for malloc, initializes the memory to 0
  * 
  * @return  void * retptr == NULL: OUT OF MEMORY
  * @return  void * retptr != NULL: pointer to buffer allocated
  */
void * pbl_malloc0(
char   * tag,        /** tag used for memory leak detection */
size_t   size        /** number of bytes to allocate        */
)
{
    void * ptr = malloc( size );
    if( !ptr )
    {
        snprintf( pbl_errstr, PBL_ERRSTR_LEN,
                  "failed to malloc %d bytes\n", size );
        pbl_errno = PBL_ERROR_OUT_OF_MEMORY;
        return( 0 );
    }

    memset( ptr, 0, size );

#ifdef PBL_MEMTRACE
    pbl_memtrace_create( tag, ptr, size );
#endif
    
    return( ptr );
}


/**
  * duplicate a buffer, similar to strdup
  *
  * @return  void * retptr == NULL: OUT OF MEMORY
  * @return  void * retptr != NULL: pointer to buffer allocated
  */
void * pbl_memdup(
char * tag,        /** tag used for memory leak detection */
void * data,       /** buffer to duplicate                */
size_t size        /** size of that buffer                */
)
{
    void * ptr = malloc( size );
    if( !ptr )
    {
        snprintf( pbl_errstr, PBL_ERRSTR_LEN,
                  "failed to malloc %d bytes\n", size );
        pbl_errno = PBL_ERROR_OUT_OF_MEMORY;
        return( 0 );
    }

    memcpy( ptr, data, size );

#ifdef PBL_MEMTRACE
    pbl_memtrace_create( tag, ptr, size );
#endif
    
    return( ptr );
}

/**
  * duplicate and concatenate two memory buffers
  *
  * @return  void * retptr == NULL: OUT OF MEMORY
  * @return  void * retptr != NULL: pointer to new buffer allocated
  */

void * pbl_mem2dup(
char * tag,        /** tag used for memory leak detection */
void * mem1,       /** first buffer to duplicate          */
size_t len1,       /** length of first buffer             */
void * mem2,       /** second buffer to duplicate         */
size_t len2        /** length of second buffer            */
)
{
    void * ret;

    if( !tag )
    {
        tag = "pbl_mem2dup";
    }

    ret = pbl_malloc( tag, len1 + len2 );
    if( !ret )
    {
        return( 0 );
    }

    if( len1 )
    {
        memcpy( ret, mem1, len1 );
    }

    if( len2 )
    {
        memcpy( ((char*)ret) + len1, mem2, len2 );
    }

    return( ret );
}

/**
 * memcpy with target length check
 *
 * @return   size_t rc: number of bytes copied
 */
size_t pbl_memlcpy(
void * to,          /** target buffer to copy to                             */
size_t tolen,       /** number of bytes in the target buffer                 */
void * from,        /** source to copy from                                  */
size_t n            /** length of source                                     */
)
{
    size_t l = n > tolen ? tolen : n;

    memcpy( to, from, l );
    return( l );
}

/**
 * find out how many starting bytes of two buffers are equal
 *  
 * @return   int rc: number of equal bytes
 */ 
        
int pbl_memcmplen(
void * left,    /** first buffer for compare               */
size_t llen,    /** length of that buffer                  */
void * right,   /** second buffer for compare              */
size_t rlen     /** length of that buffer                  */
)       
{           
    unsigned int i;
    unsigned char * l = ( unsigned char * )left;
    unsigned char * r = ( unsigned char * )right;

    if( llen > rlen )
    {
        llen = rlen;
    }

    for( i = 0; i < llen; i++ )
    {
        if( *l++ != *r++ )
        {
            break;
        }
    }

    return( i );
}

/**
 * compare two memory buffers, similar to memcmp
 *
 * @return   int rc  < 0: left is smaller than right
 * @return   int rc == 0: left and right are equal
 * @return   int rc  > 0: left is bigger than right
 */

int pbl_memcmp(
void * left,    /** first buffer for compare               */
size_t llen,    /** length of that buffer                  */
void * right,   /** second buffer for compare              */
size_t rlen     /** length of that buffer                  */
)
{
    size_t len;
    int    rc;

    /*
     * a buffer with a length 0 is logically smaller than any other buffer
     */
    if( !llen )
    {
        if( !rlen )
        {
            return( 0 );
        }
        return( -1 );
    }
    if( !rlen )
    {
        return( 1 );
    }

    /*
     * use the shorter of the two buffer lengths for the memcmp
     */
    if( llen <= rlen )
    {
        len = llen;
    }
    else
    {
        len = rlen;
    }

    /*
     * memcmp is used, therefore the ordering is ascii
     */
    rc = memcmp( left, right, len );
    if( rc )
    {
        return( rc );
    }

    /*
     * if the two buffers are equal in the first len bytes, but don't have
     * the same lengths, the longer one is logically bigger
     */
    return( (int) ( ((int)llen) - ((int)rlen) ));
}

/**
 * copy a two byte short to a two byte buffer
 */
void pbl_ShortToBuf(
unsigned char * buf,        /** buffer to copy to                 */
int s                       /** short value to copy               */
)
{
    *buf++ = ( unsigned char ) ( s >> 8 );
    *buf   = ( unsigned char ) ( s );
}

/**
 * read a two byte short from a two byte buffer
 *
 * @return int rc: the short value read
 */
int pbl_BufToShort(
unsigned char * buf            /** buffer to read from      */
)
{
    unsigned int s  = (( unsigned int ) ( *buf++ )) << 8;

    s |= *buf;
    return( s );
}

/**
 * copy a four byte long to a four byte buffer
 */
void pbl_LongToBuf(
unsigned char * buf,        /** buffer to copy to                 */
long l                      /** long value to copy                */
)
{

    *buf++ = (unsigned char ) ( ( l >> 24 ));
    *buf++ = (unsigned char ) ( ( l >> 16 ));
    *buf++ = (unsigned char ) ( ( l >>  8 ));
    *buf   = (unsigned char ) ( l );
}

/**
 * read a four byte long from a four byte buffer
 *
 * @return long ret: the long value read
 */
long pbl_BufToLong(
unsigned char * buf        /** the buffer to read from   */
)
{
    unsigned long l  = ((( unsigned long ) ( *buf++ ) )) << 24;

    l |= ((( unsigned long ) ( *buf++ ) ) ) << 16;
    l |= ((( unsigned long ) ( *buf++ ) ) ) <<  8;
    l |= *buf;

    return( l );
}

/**
 * copy a four byte long to a variable length buffer
 *
 * @return int rc: the number of bytes used in the buffer
 */
int pbl_LongToVarBuf( unsigned char * buffer, unsigned long value )
{
    if( value <= 0x7f )
    {
        *buffer = (unsigned char)value;
        return( 1 );
    }

    if( value <= 0x3fff )
    {
        *buffer++ = (unsigned char)( value / 0x100 ) | 0x80;
        *buffer = (unsigned char)value & 0xff;
        return( 2 );
    }

    if( value <= 0x1fffff )
    {
        *buffer++ = (unsigned char)( value / 0x10000 ) | 0x80 | 0x40;
        *buffer++ = (unsigned char)( value / 0x100 );
        *buffer = (unsigned char)value & 0xff;
        return( 3 );
    }

    if( value <= 0x0fffffff )
    {
        *buffer++ = (unsigned char)( value / 0x1000000 ) | 0x80 | 0x40 | 0x20;
        *buffer++ = (unsigned char)( value / 0x10000 );
        *buffer++ = (unsigned char)( value / 0x100 );
        *buffer = (unsigned char)value & 0xff;
        return( 4 );
    }

    *buffer++ = (unsigned char)0xf0;
    pbl_LongToBuf( buffer, value );

    return( 5 );
}


/**
 * read a four byte long from a variable length buffer
 *
 * @return int rc: the number of bytes used in the buffer
 */
int pbl_VarBufToLong(
unsigned char * buffer,    /** buffer to read from                 */
long * value               /** long to read to                     */
)
{
    int c = 0xff & *buffer++;
    int val;

    if( !( c & 0x80 ))
    {
        *value = c;
        return( 1 );
    }

    if( !( c & 0x40 ))
    {
        *value = ( c & 0x3f ) * 0x100 + ( *buffer & 0xff );
        return( 2 );
    }
    if( !( c & 0x20 ))
    {
        val = ( c & 0x1f ) * 0x10000;
        val += (( *buffer++ ) & 0xff ) * 0x100;
        *value = val + (( *buffer ) & 0xff );
        return( 3 );
    }

    if( !( c & 0x10 ))
    {
        val = ( c & 0x0f ) * 0x1000000;
        val += (( *buffer++ ) & 0xff ) * 0x10000;
        val += (( *buffer++ ) & 0xff ) * 0x100;
        *value = val + (( *buffer ) & 0xff );
        return( 4 );
    }

    *value = pbl_BufToLong( buffer );
    return( 5 );
}

/**
 * find out how many bytes a four byte long would use in a buffer
 *
 * @return int rc: number of bytes used in buffer
 */

int pbl_LongSize(
unsigned long value               /** value to check          */
)
{
    if( value <= 0x7f )
    {
        return( 1 );
    }

    if( value <= 0x3fff )
    {
        return( 2 );
    }

    if( value <= 0x1fffff )
    {
        return( 3 );
    }

    if( value <= 0x0fffffff )
    {
        return( 4 );
    }

    return( 5 );
}

/**
 * find out how many bytes a four byte long uses in a buffer
 *
 * @return int rc: number of bytes used in buffer
 */

int pbl_VarBufSize(
unsigned char * buffer   /** buffer to check                  */
)
{
    int c = 0xff & *buffer;

    if( !( c & 0x80 ))
    {
        return( 1 );
    }

    if( !( c & 0x40 ))
    {
        return( 2 );
    }

    if( !( c & 0x20 ))
    {
        return( 3 );
    }

    if( !( c & 0x10 ))
    {
        return( 4 );
    }

    return( 5 );
}

/*****************************************************************************/
/* #defines                                                                  */
/*****************************************************************************/
#define PBL_HASHTABLE_SIZE      1019

/*****************************************************************************/
/* typedefs                                                                  */
/*****************************************************************************/

typedef struct pbl_hashitem_s
{
    void                  * key;
    size_t                  keylen;

    void                  * data;

    struct pbl_hashitem_s * next;
    struct pbl_hashitem_s * prev;

    struct pbl_hashitem_s * bucketnext;
    struct pbl_hashitem_s * bucketprev;

} pbl_hashitem_t;

typedef struct pbl_hashbucket_s
{
    pbl_hashitem_t * head;
    pbl_hashitem_t * tail;

} pbl_hashbucket_t;

struct pbl_hashtable_s
{
    char             * magic;
    int                currentdeleted;
    pbl_hashitem_t   * head;
    pbl_hashitem_t   * tail;
    pbl_hashitem_t   * current;
    pbl_hashbucket_t * buckets;

};
typedef struct pbl_hashtable_s pbl_hashtable_t;
    
/*****************************************************************************/
/* globals                                                                   */
/*****************************************************************************/

/*****************************************************************************/
/* functions                                                                 */
/*****************************************************************************/

static int hash( const unsigned char * key, size_t keylen )
{
    int ret = 104729;

    for( ; keylen-- > 0; key++ )
    {
        if( *key )
        {
            ret *= *key + keylen;
            ret %= PBL_HASHTABLE_SIZE;
        }
    }

    return( ret % PBL_HASHTABLE_SIZE );
}

/**
 * create a new hash table
 *
 * @return pblHashTable_t * retptr != NULL: pointer to new hash table
 * @return pblHashTable_t * retptr == NULL: OUT OF MEMORY
 */
pblHashTable_t * pblHtCreate( void )
{
    pbl_hashtable_t * ht;

    ht = (pbl_hashtable_t*)pbl_malloc0( "pblHtCreate hashtable", sizeof( pbl_hashtable_t ) );
    if( !ht )
    {
        return( 0 );
    }

    ht->buckets = (pbl_hashbucket_t *)pbl_malloc0( "pblHtCreate buckets",
                               sizeof( pbl_hashbucket_t ) * PBL_HASHTABLE_SIZE);
    if( !ht->buckets )
    {
        PBL_FREE( ht );
        return( 0 );
    }

    /*
     * set the magic marker of the hashtable
     */
    ht->magic = rcsid;

    return( ( pblHashTable_t * )ht );
}

/**
 * insert a key / data pair into a hash table
 *
 * only the pointer to the data is stored in the hash table
 * no space is malloced for the data!
 *
 * @return  int ret == 0: ok
 * @return  int ret == -1: an error, see pbl_errno:
 * @return    PBL_ERROR_EXISTS:        an item with the same key already exists
 * @return    PBL_ERROR_OUT_OF_MEMORY: out of memory
 */

int pblHtInsert(
pblHashTable_t          * h,      /** hash table to insert to             */
void                    * key,    /** key to insert                       */
size_t                    keylen, /** length of that key                  */
void                    * dataptr /** dataptr to insert                   */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;
    pbl_hashbucket_t * bucket = 0;
    pbl_hashitem_t   * item = 0;

    int                hashval = hash((const unsigned char *) key, keylen );

    bucket = ht->buckets + hashval;

    if( keylen < (size_t)1 )
    {
        /*
         * the length of the key can not be smaller than 1
         */
        pbl_errno = PBL_ERROR_EXISTS;
        return( -1 );
    }

    for( item = bucket->head; item; item = item->bucketnext )
    {
        if(( item->keylen == keylen ) && !memcmp( item->key, key, keylen ))
        {
            snprintf( pbl_errstr, PBL_ERRSTR_LEN,
                      "insert of duplicate item in hashtable\n" );
            pbl_errno = PBL_ERROR_EXISTS;
            return( -1 );
        }
    }

    item = (pbl_hashitem_t*)pbl_malloc0( "pblHtInsert hashitem", sizeof( pbl_hashitem_t ) );
    if( !item )
    {
        return( -1 );
    }

    item->key = pbl_memdup( "pblHtInsert item->key", key, keylen );
    if( !item->key )
    {
        PBL_FREE( item );
        return( -1 );
    }
    item->keylen = keylen;
    item->data = dataptr;

    /*
     * link the item
     */
    PBL_LIST_APPEND( bucket->head, bucket->tail, item, bucketnext, bucketprev );
    PBL_LIST_APPEND( ht->head, ht->tail, item, next, prev );

    ht->current = item;
    return( 0 );
}

/**
 * search for a key in a hash table
 *
 * @return void * retptr != NULL: pointer to data of item found
 * @return void * retptr == NULL: no item found with the given key
 * @return     PBL_ERROR_NOT_FOUND:
 */

void * pblHtLookup(
pblHashTable_t              * h,      /** hash table to search in          */
void                        * key,    /** key to search                    */
size_t                        keylen  /** length of that key               */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;
    pbl_hashbucket_t * bucket = 0;
    pbl_hashitem_t   * item = 0;

    int                hashval = hash((const unsigned char *) key, keylen );

    bucket = ht->buckets + hashval;

    for( item = bucket->head; item; item = item->bucketnext )
    {
        if(( item->keylen == keylen ) && !memcmp( item->key, key, keylen ))
        {
            ht->current = item;
            ht->currentdeleted = 0;
            return( item->data );
        }
    }
            
    pbl_errno = PBL_ERROR_NOT_FOUND;

    return( 0 );
}

/**
 * get data of first key in hash table
 *
 * This call and \Ref{pblHtNext} can be used in order to loop through all items
 * stored in a hash table.
 *
 * <PRE>
   Example:

   for( data = pblHtFirst( h ); data; data = pblHtNext( h ))
   {
       do something with the data pointer
   }
   </PRE>

 * @return void * retptr != NULL: pointer to data of first item
 * @return void * retptr == NULL: the hash table is empty
 * @return     PBL_ERROR_NOT_FOUND:
 */

void * pblHtFirst(
pblHashTable_t              * h       /** hash table to look in            */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;
    pbl_hashitem_t   * item = 0;

    item = ht->head;
    if( item )
    {
        ht->current = item;
        ht->currentdeleted = 0;
        return( item->data );
    }

    pbl_errno = PBL_ERROR_NOT_FOUND;
    return( 0 );
}

/**
 * get data of next key in hash table
 *
 * This call and \Ref{pblHtFirst} can be used in order to loop through all items
 * stored in a hash table.
 *
 * <PRE>
   Example:

   for( data = pblHtFirst( h ); data; data = pblHtNext( h ))
   {
       do something with the data pointer
   }
   </PRE>

 * @return void * retptr != NULL: pointer to data of next item
 * @return void * retptr == NULL: there is no next item in the hash table
 * @return     PBL_ERROR_NOT_FOUND: 
 */

void * pblHtNext(
pblHashTable_t              * h       /** hash table to look in            */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;
    pbl_hashitem_t   * item = 0;

    if( ht->current )
    {
        if( ht->currentdeleted )
        {
            item = ht->current;
        }
        else
        {
            item = ht->current->next;
        }
        ht->currentdeleted = 0;
    }
    if( item )
    {
        ht->current = item;
        return( item->data );
    }

    pbl_errno = PBL_ERROR_NOT_FOUND;
    return( 0 );
}

/**
 * get data of current key in hash table
 *
 * @return void * retptr != NULL: pointer to data of current item
 * @return void * retptr == NULL: there is no current item in the hash table
 * @return     PBL_ERROR_NOT_FOUND:
 */

void * pblHtCurrent(
pblHashTable_t              * h       /** hash table to look in            */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;

    if( ht->current )
    {
        return( ht->current->data );
    }

    pbl_errno = PBL_ERROR_NOT_FOUND;
    return( 0 );
}

void * pblHtCurrentKey(
pblHashTable_t              * h       /** hash table to look in            */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;

    if( ht->current )
    {
        return( ht->current->key );
    }

    pbl_errno = PBL_ERROR_NOT_FOUND;
    return( 0 );
}

/**
 * remove an item from the hash table
 *
 * parameters key and keylen are optional, if they are not given
 * the current record is deleted
 *
 * if the current record is removed the pointer to the current record
 * is moved to the next record.
 *
 * <PRE>
   Example:

   for( data = pblHtFirst( h ); data; data = pblHtRemove( h, 0, 0 ))
   {
       this loop removes all items from a hash table
   }
   </PRE>
 *
 * if the current record is moved by this function the next call to
 * \Ref{pblHtNext} will return the data of the then current record.
 * Therefore the following code does what is expected:
 * It visits all items of the hash table and removes the expired ones.
 *
 * <PRE>
   for( data = pblHtFirst( h ); data; data = pblHtNext( h ))
   {
       if( needs to be deleted( data ))
       {
           pblHtRemove( h, 0, 0 );
       }
   }
   </PRE>

 * @return int ret == 0: ok
 * @return int ret == -1: an error, see pbl_errno:
 * @return     PBL_ERROR_NOT_FOUND: the current item is not positioned
 * @return                          or there is no item with the given key
 */

int pblHtRemove(
pblHashTable_t            * h,     /** hash table to remove from           */
void                      * key,   /** OPT: key to remove                  */
size_t                      keylen /** OPT: length of that key             */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;
    pbl_hashbucket_t * bucket = 0;
    pbl_hashitem_t   * item = 0;

    int                hashval = 0;

    if( keylen && key )
    {
        hashval = hash( (const unsigned char *)key, keylen );
        bucket = ht->buckets + hashval;

        for( item = bucket->head; item; item = item->bucketnext )
        {
            if(( item->keylen == keylen ) && !memcmp( item->key, key, keylen ))
            {
                break;
            }
        }
    }
    else
    {
        item = ht->current;

        if( item )
        {
            hashval = hash( (const unsigned char *)item->key, item->keylen );
            bucket = ht->buckets + hashval;
        }
    }

    if( item )
    {
        if( item == ht->current )
        {
            ht->currentdeleted = 1;
            ht->current = item->next;
        }

        /*
         * unlink the item
         */
        PBL_LIST_UNLINK( bucket->head, bucket->tail, item,
                         bucketnext, bucketprev );
        PBL_LIST_UNLINK( ht->head, ht->tail, item, next, prev );

        PBL_FREE( item->key );
        PBL_FREE( item );
        return( 0 );
    }

    pbl_errno = PBL_ERROR_NOT_FOUND;
    return( -1 );
}

/**
 * delete a hash table
 *
 * the hash table has to be empty!
 *
 * @return int ret == 0: ok
 * @return int ret == -1: an error, see pbl_errno:
 * @return     PBL_ERROR_EXISTS: the hash table is not empty
 */

int pblHtDelete(
pblHashTable_t * h        /** hash table to delete */
)
{
    pbl_hashtable_t  * ht = ( pbl_hashtable_t * )h;

    if( ht->head )
    {
        pbl_errno = PBL_ERROR_EXISTS;
        return( -1 );
    }

    PBL_FREE( ht->buckets );
    PBL_FREE( ht );

    return( 0 );
}



syntax highlighted by Code2HTML, v. 0.9.1