/*___INFO__MARK_BEGIN__*/
/*************************************************************************
 * 
 *  The Contents of this file are made available subject to the terms of
 *  the Sun Industry Standards Source License Version 1.2
 * 
 *  Sun Microsystems Inc., March, 2001
 * 
 * 
 *  Sun Industry Standards Source License Version 1.2
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.2 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
 * 
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 * 
 *   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 * 
 *   Copyright: 2001 by Sun Microsystems, Inc.
 * 
 *   All Rights Reserved.
 * 
 ************************************************************************/
/*___INFO__MARK_END__*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>

/* do not compile in monitoring code */
#ifndef NO_SGE_COMPILE_DEBUG
#define NO_SGE_COMPILE_DEBUG
#endif

#include "msg_cull.h"
#include "sgermon.h"
#include "sge_log.h"
#include "sge_string.h"
#include "cull_db.h"
#include "cull_sortP.h"
#include "cull_where.h"
#include "cull_listP.h"
#include "cull_multitypeP.h"
#include "cull_whatP.h"
#include "cull_lerrnoP.h"
#include "cull_hash.h"
#include "cull_state.h"
#include "pack.h"
#include "cull_pack.h"

#define CULL_BASIS_LAYER CULL_LAYER


static void lWriteList_(const lList *lp, dstring *buffer, int nesting_level);
static void lWriteElem_(const lListElem *lp, dstring *buffer, int nesting_level);

/****** cull/list/-Field_Attributes *************************************************
*  NAME
*     Field_Attributes -- Attributes of cull type fields
*
*  SYNOPSIS
*     When a field of a cull object type is defined, any number of 
*     attributes can be given to the field.
*
*     The syntax of the field definition is
*        <typedef>(<field_name>, <attrib1> [|<attrib2> ...]
*     e.g.
*        SGE_STRING(QU_qname, CULL_HASH | CULL_UNIQUE)
*
*     The following attributes can be given to a field:
*        CULL_DEFAULT
*           no special settings - default behaviour
*        CULL_PRIMARY_KEY
*           the field is part of the primary key
*           does *not* imply uniqueness or hashing
*        CULL_HASH          
*           a hash table will be created on the field for lists of the 
*           object type (non unique, unless explicitly specified by CULL_UNIQUE)
*        CULL_UNIQUE        
*           the field value has to be unique for all objects in a list
*           currently only used for the definition of hash tables,
*           but it could be used for general consistency checks.
*        CULL_JGDI_HIDDEN
*        CULL_CONFIGURE     
*           the field can be changed by configuration functions
*           not yet implemented
*        CULL_SPOOL         
*           the field is spooled
*        CULL_SUBLIST
*           the field is spooled when the type is used as subtype in another
*           type, but less fields shall be spooled, e.g. in the CE_TYPE:
*           all fields are spooled if the complex variable definition is spooled,
*           but only CE_name and CE_stringval are spooled when used as subtype,
*           like in the complex_values of exec host or queue
*        CULL_SPOOL_PROJECT
*           deprecated?
*        CULL_SPOOL_USER
*           deprecated?
*        CULL_JGDI_RO
*        CULL_JGDI_CONF
*
*  NOTES
*     Further attributes can be introduced as necessary, e.g.
*        CULL_ARRAY - the field is an array of the specified data type
*
*  SEE ALSO
*     cull/list/mt_get_type()
*     cull/list/mt_do_hashing()
*     cull/list/mt_is_unique()
******************************************************************************/

/****** cull/list/lCopyElem() *************************************************
*  NAME
*     lCopyElem() -- Copies a whole list element 
*
*  SYNOPSIS
*     lListElem* lCopyElem(const lListElem *ep) 
*
*  FUNCTION
*     Copies a whole list element 
*
*  INPUTS
*     const lListElem *ep - element 
*
*  RESULT
*     lListElem* - copy of 'ep'
******************************************************************************/
lListElem *lCopyElem(const lListElem *ep) {
   return lCopyElemHash(ep, true);
}

/****** cull/list/lCopyElemHash() *************************************************
*  NAME
*     lCopyElemHash() -- Copies a whole list element 
*
*  SYNOPSIS
*     lListElem* lCopyElemHash(const lListElem *ep, bool isHash) 
*
*  FUNCTION
*     Copies a whole list element 
*
*  INPUTS
*     const lListElem *ep - element 
*     bool                - generate hash or not
*
*  RESULT
*     lListElem* - copy of 'ep'
******************************************************************************/
lListElem *lCopyElemHash(const lListElem *ep, bool isHash) 
{
   lListElem *new;
   int index;
   int max;

   DENTER(CULL_LAYER, "lCopyElem");

   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN(NULL);
   }

   max = lCountDescr(ep->descr); 

   if (!(new = lCreateElem(ep->descr))) {
      LERROR(LECREATEELEM);
      DRETURN(NULL);
   }

   for (index = 0; index<max ; index++) { 
      if (lCopySwitchPack(ep, new, index, index, isHash, NULL, NULL) != 0) {
         lFreeElem(&new);

         LERROR(LECOPYSWITCH);
         DRETURN(NULL);
      }
   }
   if (!sge_bitfield_copy(&(ep->changed), &(new->changed))) {
      lFreeElem(&new);

      LERROR(LECOPYSWITCH);
      DRETURN(NULL);
   }

   new->status = FREE_ELEM;

   DRETURN(new);
}

/****** cull/list/lModifyWhat() ************************************************
*  NAME
*     lModifyWhat() -- Copy parts of an element 
*
*  SYNOPSIS
*     int lModifyWhat(lListElem *dst, const lListElem *src, 
*                     const lEnumeration *enp) 
*
*  FUNCTION
*     Copies elements from 'src' to 'dst' using the enumeration 'enp'
*     as a mask or copies all elements if 'enp' is NULL 
*
*  INPUTS
*     lListElem *dst          - destination element
*     const lListElem *src    - source element
*     const lEnumeration *enp - mask 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
******************************************************************************/
int lModifyWhat(lListElem *dst, const lListElem *src, const lEnumeration *enp) 
{
   int ret, i = 0;

   DENTER(CULL_LAYER, "lModifyWhat");

   ret = lCopyElemPartialPack(dst, &i, src, enp, true, NULL);

   DRETURN(ret);
}

/****** cull/list/lCopyElemPartialPack() **************************************
*  NAME
*     lCopyElemPartialPack() -- Copies parts of an element 
*
*  SYNOPSIS
*     int 
*     lCopyElemPartialPack(lListElem *dst, int *jp, const lListElem *src, 
*                          const lEnumeration *enp, bool isHash,
*                          sge_pack_buffer *pb) 
*
*  FUNCTION
*     Copies elements from list element 'src' to 'dst' using the
*     enumeration 'enp' as a mask or copies all elements if
*     'enp' is NULL. Copying starts at index *jp. If pb is not NULL
*     then the elements will be stored in the packbuffer 'pb' instead of
*     being copied.
*
*  INPUTS
*     lListElem *dst          - destination element 
*     int *jp                 - Where should the copy operation start 
*     const lListElem *src    - src element 
*     const lEnumeration *enp - enumeration 
*     bool                    - generate hash or not
*     sge_pack_buffer *pb     - packbuffer
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*******************************************************************************/
int 
lCopyElemPartialPack(lListElem *dst, int *jp, const lListElem *src, 
                     const lEnumeration *enp, bool isHash,
                     sge_pack_buffer *pb) 
{
   int i;

   DENTER(CULL_LAYER, "lCopyElemPartialPack");

   if (!enp || (!dst && !pb) || !jp) {
      LERROR(LEENUMNULL);
      DRETURN(-1);
   }

   switch (enp[0].pos) {
   case WHAT_ALL:               /* all fields of element src is copied */
      if (pb == NULL) {
         for (i = 0; src->descr[i].nm != NoName; i++, (*jp)++) {
            if (lCopySwitchPack(src, dst, i, *jp, isHash, enp[0].ep, NULL) != 0) {
               LERROR(LECOPYSWITCH);
               DRETURN(-1);
            }
#if 0 /* TODO EB: we need to decide if we want this or not */
            /* copy changed field information */
            if(sge_bitfield_get(&(src->changed), i)) {
               sge_bitfield_set(&(dst->changed), *jp);
            }
#endif
         }
      } else {
         cull_pack_elem(pb, src);
      }
      break;

   case WHAT_NONE:              /* no field of element src is copied */
      break;

   default:                     /* copy only the in enp enumerated elems */
      if (pb == NULL) {
         int maxpos = 0;
         maxpos = lCountDescr(src->descr);

         for (i = 0; enp[i].nm != NoName; i++, (*jp)++) {
            if (enp[i].pos > maxpos || enp[i].pos < 0) {
               LERROR(LEENUMDESCR);
               DRETURN(-1);
            }

            if (lCopySwitchPack(src, dst, enp[i].pos, *jp, isHash, enp[i].ep, NULL) != 0) {
               LERROR(LECOPYSWITCH);
               DRETURN(-1);
            }
#if 0 /* TODO EB: we need to decide if we want this or not */
            /* copy changed field information */
            if(sge_bitfield_get(&(src->changed), enp[i].pos)) {
               sge_bitfield_set(&(dst->changed), *jp);
            }
#endif
         }
      } else {
         cull_pack_elem_partial(pb, src, enp, 0);
      }
   }
   DRETURN(0);
}

/****** cull/list/lCopySwitchPack() *******************************************
*  NAME
*     lCopySwitchPacPackk() -- Copy parts of elements indedendent from type 
*
*  SYNOPSIS
*     int 
*     lCopySwitchPack(const lListElem *sep, lListElem *dep, int src_idx, 
*                 int dst_idx, bool isHash, sge_pack_buffer *pb) 
*
*  FUNCTION
*     Copies from the element 'sep' (using index 'src_idx') to
*     the element 'dep' (using index 'dst_idx') in dependence 
*     of the type or it copies the it directly into pb. 
*
*  INPUTS
*     const lListElem *sep - source element 
*     lListElem *dep       - destination element 
*     int src_idx          - source index 
*     int dst_idx          - destination index 
*     bool isHash          - create Hash or not
*     lEnumeration *ep     - enumeration oiter to be used for sublists
*     sge_pack_buffer *pb  - pack buffer
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*******************************************************************************/
int 
lCopySwitchPack(const lListElem *sep, lListElem *dep, int src_idx, int dst_idx, 
                bool isHash, lEnumeration *ep, sge_pack_buffer *pb) 
{
   lList *tlp;
   lListElem *tep;

   DENTER(CULL_LAYER, "lCopySwitchPack");

   if ((!dep && !pb) || !sep) {
      DRETURN(-1);
   }

   switch (mt_get_type(dep->descr[dst_idx].mt)) {
   case lUlongT:
      dep->cont[dst_idx].ul = sep->cont[src_idx].ul;
      break;
   case lStringT:
      if (!sep->cont[src_idx].str)
         dep->cont[dst_idx].str = NULL;
      else
         dep->cont[dst_idx].str = strdup(sep->cont[src_idx].str);
      break;
   case lHostT:
      if (!sep->cont[src_idx].host)
         dep->cont[dst_idx].host = NULL;
      else
         dep->cont[dst_idx].host = strdup(sep->cont[src_idx].host);
      break;

   case lDoubleT:
      dep->cont[dst_idx].db = sep->cont[src_idx].db;
      break;
   case lListT:
      if ((tlp = sep->cont[src_idx].glp) == NULL) 
         dep->cont[dst_idx].glp = NULL;
      else {
         dep->cont[dst_idx].glp = lSelectHashPack(tlp->listname, tlp, NULL, 
                                                  ep, isHash, pb);
      }
      break;
   case lObjectT:
      if ((tep = sep->cont[src_idx].obj) == NULL) {
         dep->cont[dst_idx].obj = NULL;
      } else {
         lListElem *new = lSelectElemPack(tep, NULL, ep, isHash, pb);
         new->status = OBJECT_ELEM;
         dep->cont[dst_idx].obj = new;
      }   
      break;
   case lIntT:
      dep->cont[dst_idx].i = sep->cont[src_idx].i;            
      break;
   case lFloatT:
      dep->cont[dst_idx].fl = sep->cont[src_idx].fl;
      break;
   case lLongT:
      dep->cont[dst_idx].l = sep->cont[src_idx].l;
      break;
   case lCharT:
      dep->cont[dst_idx].c = sep->cont[src_idx].c;
      break;
   case lBoolT:
      dep->cont[dst_idx].b = sep->cont[src_idx].b;
      break;
   case lRefT:
      dep->cont[dst_idx].ref = sep->cont[src_idx].ref;
      break;
   default:
      DRETURN(-1);
   }

   DRETURN(0);
}

/****** cull/list/lGetListName() **********************************************
*  NAME
*     lGetListName() -- returns the user defined name of a list 
*
*  SYNOPSIS
*     const char* lGetListName(const lList *lp) 
*
*  FUNCTION
*     Returns the user defined name of a list. 
*
*  INPUTS
*     const lList *lp - list pointer 
*
*  RESULT
*     const char* - list name
******************************************************************************/
const char *lGetListName(const lList *lp) 
{
   DENTER(CULL_LAYER, "lGetListName");

   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN("No List specified");
   }

   if(!lp->listname) {
      LERROR(LENULLSTRING);
      DRETURN("No list name specified");
   }

   DRETURN(lp->listname);
}

/****** cull/list/lGetListDescr() *********************************************
*  NAME
*     lGetListDescr() -- Returns the descriptor of a list 
*
*  SYNOPSIS
*     const lDescr* lGetListDescr(const lList *lp) 
*
*  FUNCTION
*     Returns the descriptor of a list 
*
*  INPUTS
*     const lList *lp - list pointer 
*
*  RESULT
*     const lDescr* - destriptor 
*  
*  NOTES
*     MT-NOTE: lGetListDescr() is MT safe
******************************************************************************/
const lDescr *lGetListDescr(const lList *lp) 
{
   DENTER(CULL_LAYER, "lGetListDescr");

   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN(NULL);
   }
   DRETURN(lp->descr);
}

/****** cull/list/lGetNumberOfElem() ******************************************
*  NAME
*     lGetNumberOfElem() -- Returns the number of elements in a list 
*
*  SYNOPSIS
*     int lGetNumberOfElem(const lList *lp) 
*
*  FUNCTION
*     Returns the number of elements in a list 
*
*  INPUTS
*     const lList *lp - list pointer 
*
*  RESULT
*     int - number of elements 
* 
*  NOTES
*     MT-NOTE: lGetNumberOfElem() is MT safe
******************************************************************************/
int lGetNumberOfElem(const lList *lp) 
{
   DENTER(CULL_LAYER, "lGetNumberOfElem");

   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN(0);
   }

   DRETURN(lp->nelem);
}

/****** cull/list/lGetElemIndex() *********************************************
*  NAME
*     lGetElemIndex() -- returns the index of element in list lp 
*
*  SYNOPSIS
*     int lGetElemIndex(const lListElem* ep, const lList* lp) 
*
*  FUNCTION
*     returns the index of element in list lp 
*
*  INPUTS
*     lListElem* ep - element 
*     lList* lp     - list 
*
*  RESULT
*     index number 
******************************************************************************/
int lGetElemIndex(const lListElem *ep, const lList *lp) 
{
   int i = -1;
   lListElem *ep2;

   DENTER(CULL_LAYER, "lGetElemIndex");

   if (!ep || ep->status != BOUND_ELEM) {
      DRETURN(-1);
   }

   for_each(ep2, lp) {
      i++;
      if (ep2 == ep)
         break;
   }

   DRETURN(i);
}

/****** cull/list/lGetNumberOfRemainingElem() *********************************
*  NAME
*     lGetNumberOfRemainingElem() -- Number of following elements 
*
*  SYNOPSIS
*     int lGetNumberOfRemainingElem(const lListElem *ep) 
*
*  FUNCTION
*     Returns the number of elements behind an element 
*
*  INPUTS
*     const lListElem *ep - element 
*
*  RESULT
*     int - number of elements 
******************************************************************************/
int lGetNumberOfRemainingElem(const lListElem *ep) 
{
   int i = 0;

   DENTER(CULL_LAYER, "lGetNumberOfElem");

   if (!ep) {
      DRETURN(0);
   }

   while ((ep = lNext(ep)))
      i++;

   DRETURN(i);
}

/****** cull/list/lGetElemDescr() **********************************************
*  NAME
*     lGetElemDescr() -- returns the descriptor of a list element 
*
*  SYNOPSIS
*     const lDescr* lGetElemDescr(const lListElem* ep) 
*
*  FUNCTION
*     returns the descriptor of a list element 
*
*  INPUTS
*     lListElem* ep - CULL element 
*
*  RESULT
*     Pointer to descriptor 
*
******************************************************************************/
const lDescr *lGetElemDescr(const lListElem *ep) 
{
   DENTER(CULL_LAYER, "lGetListDescr");

   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN(NULL);
   }
   DRETURN(ep->descr);
}

/****** cull/list/lWriteElem() ************************************************
*  NAME
*     lWriteElem() -- Write a element to monitoring level CULL_LAYER 
*
*  SYNOPSIS
*     void lWriteElem(const lListElem *ep) 
*
*  FUNCTION
*     Write a element to monitoring level CULL_LAYER a info message 
*
*  INPUTS
*     const lListElem *ep - element 
******************************************************************************/
void lWriteElem(const lListElem *ep) 
{
   dstring buffer = DSTRING_INIT;
   const char *str;

   DENTER(CULL_LAYER, "lWriteElem");
   lWriteElem_(ep, &buffer, 0);   
   str = sge_dstring_get_string(&buffer);
   if (str != NULL) { 
      fprintf(stderr, "%s", str);
   }
   sge_dstring_free(&buffer); 
   DRETURN_VOID;
}

/****** cull/list/lWriteElemTo() **********************************************
*  NAME
*     lWriteElemTo() -- Write a element to file stream 
*
*  SYNOPSIS
*     void lWriteElemTo(const lListElem *ep, FILE *fp) 
*
*  FUNCTION
*     Write a element to file stream 
*
*  INPUTS
*     const lListElem *ep - element 
*     FILE *fp            - file stream 
*     ???/???
******************************************************************************/
void lWriteElemTo(const lListElem *ep, FILE *fp) 
{
   dstring buffer = DSTRING_INIT;
   const char *str;

   DENTER(CULL_LAYER, "lWriteElemTo");
   lWriteElem_(ep, &buffer, 0);
   str = sge_dstring_get_string(&buffer);
   if (str != NULL) { 
      fprintf(fp, "%s", str);
   }
   sge_dstring_free(&buffer);
   DRETURN_VOID;
}

void lWriteElemToStr(const lListElem *ep, dstring *buffer) {
   DENTER(CULL_LAYER, "lWriteElemToStr");
   lWriteElem_(ep, buffer, 0);
   DRETURN_VOID;
}

static void lWriteElem_(const lListElem *ep, dstring *buffer, int nesting_level) 
{
   int i;
   char space[128];
   lList *tlp;
   lListElem *tep;
   const char *str;

   DENTER(TOP_LAYER, "lWriteElem");

   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN_VOID;
   }

   for (i = 0; i < nesting_level * 3; i++) {
      space[i] = ' ';
   }
   space[i] = '\0';

   sge_dstring_sprintf_append(buffer, "%s-------------------------------\n", space);

   for (i = 0; mt_get_type(ep->descr[i].mt) != lEndT; i++) {
      bool changed = sge_bitfield_get(&(ep->changed), i);
      const char *name = ((lNm2Str(ep->descr[i].nm) != NULL) ? lNm2Str(ep->descr[i].nm) : "(null)");

      switch (mt_get_type(ep->descr[i].mt)) {
      case lIntT:
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Integer) %c = %d\n", space, name, changed ? '*' : ' ', lGetPosInt(ep, i));
         break;
      case lUlongT:
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Ulong)   %c = " sge_u32"\n", space, name, changed ? '*' : ' ', lGetPosUlong(ep, i));
         break;
      case lStringT:
         str = lGetPosString(ep, i);
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (String)  %c = %s\n", space, name, changed ? '*' : ' ', str ? str : "(null)");
         break;
      case lHostT:
         str = lGetPosHost(ep, i);
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Host)    %c = %s\n", space, name, changed ? '*' : ' ', str ? str : "(null)");
         break;
      case lListT:
         tlp = lGetPosList(ep, i);
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (List)    %c = %s\n", space, name, changed ? '*' : ' ', tlp ? "full {" : "empty");
         if (tlp) {
            lWriteList_(tlp, buffer, nesting_level + 1);
            sge_dstring_sprintf_append(buffer, "%s}\n", space);
         }
         break;
      case lObjectT:
         tep = lGetPosObject(ep, i);
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Object)  %c = %s\n", space, name, changed ? '*' : ' ', tep ? "object {" : "none");
         if (tep) {
            lWriteElem_(tep, buffer, nesting_level + 1);
            sge_dstring_sprintf_append(buffer, "%s}\n", space);
         }
         break;
      case lFloatT:
DTRACE;
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Float)   %c = %f\n", space, name, changed ? '*' : ' ', lGetPosFloat(ep, i));
         break;
      case lDoubleT:
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Double)  %c = %f\n", space, name, changed ? '*' : ' ', lGetPosDouble(ep, i));
         break;
      case lLongT:
DTRACE;
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Long)    %c = %ld\n", space, name, changed ? '*' : ' ', lGetPosLong(ep, i));
         break;
      case lBoolT:
DTRACE;
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Bool)    %c = %s\n", space, name, changed ? '*' : ' ', lGetPosBool(ep, i) ? "true" : "false");
         break;
      case lCharT:
DTRACE;
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Char)    %c = %c\n", space, name, changed ? '*' : ' ', isprint (lGetPosChar(ep, i)) ? lGetPosChar(ep, i) : '?');
         break;
      case lRefT:
DTRACE;
         sge_dstring_sprintf_append(buffer, "%s%-20.20s (Ref)     %c = %p\n", space, name, changed ? '*' : ' ', lGetPosRef(ep, i));
         break;
      default:
DTRACE;
         unknownType("lWriteElem");
      }
   }

   DRETURN_VOID;
}

/****** cull/list/lWriteList() ************************************************
*  NAME
*     lWriteList() -- Write a list to monitoring level CULL_LAYER 
*
*  SYNOPSIS
*     void lWriteList(const lList *lp) 
*
*  FUNCTION
*     Write a list to monitoring level CULL_LAYER as info message. 
*
*  INPUTS
*     const lList *lp - list 
******************************************************************************/
void lWriteList(const lList *lp) 
{
   dstring buffer = DSTRING_INIT;
   const char *str;

   DENTER(CULL_LAYER, "lWriteList");
   if (!lp) {
      DRETURN_VOID;
   }
   lWriteList_(lp, &buffer, 0);
   str = sge_dstring_get_string(&buffer);
   if (str != NULL) {
      fprintf(stderr, "%s", str);
   }
   sge_dstring_free(&buffer);
   DRETURN_VOID;
}

/****** cull/list/lWriteListTo() **********************************************
*  NAME
*     lWriteListTo() -- Write a list to a file stream 
*
*  SYNOPSIS
*     void lWriteListTo(const lList *lp, FILE *fp) 
*
*  FUNCTION
*     Write a list to a file stream 
*
*  INPUTS
*     const lList *lp - list 
*     FILE *fp        - file stream 
*******************************************************************************/
void lWriteListTo(const lList *lp, FILE *fp) 
{
   dstring buffer = DSTRING_INIT;
   const char *str;

   DENTER(CULL_LAYER, "lWriteListTo");
   lWriteList_(lp, &buffer, 0);
   str = sge_dstring_get_string(&buffer);
   if (str != NULL) {
      fprintf(fp, "%s", str);
   }
   sge_dstring_free(&buffer);
   DRETURN_VOID;
}

void lWriteListToStr(const lList* lp, dstring* buffer) {
   DENTER(CULL_LAYER, "lWriteListToStr");
   lWriteList_(lp, buffer, 0);
   DRETURN_VOID;
}

static void lWriteList_(const lList *lp, dstring *buffer, int nesting_level) 
{
   lListElem *ep;
   char indent[128];
   int i;

   DENTER(CULL_LAYER, "lWriteList_");
   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN_VOID;
   }
   for (i = 0; i < nesting_level * 3; i++) {
      indent[i] = ' ';
   }
   indent[i] = '\0';

   sge_dstring_sprintf_append(buffer, "\n%sList: <%s> %c #Elements: %d\n",    
                              indent, (lGetListName(lp) != NULL) ? lGetListName(lp) : "NULL", 
                              lp->changed ? '*' : ' ', lGetNumberOfElem(lp));
   for_each(ep, lp) {
      lWriteElem_(ep, buffer, nesting_level);
   }
   DRETURN_VOID;
}

/****** cull/list/lCreateElem() ***********************************************
*  NAME
*     lCreateElem() -- Create an element for a specific list 
*
*  SYNOPSIS
*     lListElem* lCreateElem(const lDescr *dp) 
*
*  FUNCTION
*     Create an element for a specific list 
*
*  INPUTS
*     const lDescr *dp - descriptor 
*
*  RESULT
*     lListElem* - element pointer or NULL
******************************************************************************/
lListElem *lCreateElem(const lDescr *dp) 
{
   int n, i;
   lListElem *ep;

   DENTER(CULL_LAYER, "lCreateElem");

   if ((n = lCountDescr(dp)) <= 0) {
      LERROR(LECOUNTDESCR);
      DRETURN(NULL);
   }

   ep = (lListElem *) malloc(sizeof(lListElem));

   if (ep == NULL) {
      LERROR(LEMALLOC);
      DRETURN(NULL);
   }

   ep->next = NULL;
   ep->prev = NULL;
 
   ep->descr = (lDescr *) malloc(sizeof(lDescr) * (n + 1));
   if (!ep->descr) {
      LERROR(LEMALLOC);
      free(ep);
      DRETURN(NULL);
   }
   memcpy(ep->descr, dp, sizeof(lDescr) * (n + 1));

   /* new descr has no htables yet */
   for (i = 0; i <= n; i++) {
      ep->descr[i].ht = NULL;
      ep->descr[i].mt |= (dp->mt & CULL_IS_REDUCED);
   }
                  
   ep->status = FREE_ELEM;
   if (!(ep->cont = (lMultiType *) calloc(1, sizeof(lMultiType) * n))) {
      LERROR(LEMALLOC);
      free(ep->descr);
      free(ep);
      DRETURN(NULL);
   }

   if(!sge_bitfield_init(&(ep->changed), n)) {
      LERROR(LEMALLOC);
      free(ep->cont);
      free(ep->descr);
      free(ep);
      DRETURN(NULL);
   }

   DRETURN(ep);
}

/****** cull/list/lCreateList() ***********************************************
*  NAME
*     lCreateList() -- Create an empty list 
*
*  SYNOPSIS
*     lList* lCreateList(const char *listname, const lDescr *descr) 
*
*  FUNCTION
*     Create an empty list with a given descriptor and a user defined
*     listname. 
*
*  INPUTS
*     const char *listname - list name 
*     const lDescr *descr  - descriptor 
*
*  RESULT
*     lList* - list pointer or NULL 
*******************************************************************************/
lList *lCreateList(const char *listname, const lDescr *descr) 
{
   return lCreateListHash(listname, descr, true);
}

/****** cull/list/lCreateListHash() ********************************************
*  NAME
*     lCreateList() -- Create an empty list
*
*  SYNOPSIS
*     lList* lCreateList(const char *listname, const lDescr *descr, bool hash) 
*
*  FUNCTION
*     Create an empty list with a given descriptor and a user defined
*     listname. 
*     The caller can choose whether hashtables shall be created or not.
*
*  INPUTS
*     const char *listname - list name 
*     const lDescr *descr  - descriptor 
*     bool hash            - shall hashtables be created?
*
*  RESULT
*     lList* - list pointer or NULL 
*******************************************************************************/
lList *lCreateListHash(const char *listname, const lDescr *descr, bool hash) 
{
   lList *lp;
   int i, n;

   DENTER(CULL_LAYER, "lCreateListHash");

   if (listname == NULL) {
      listname = "No list name specified";
   }

   if (!descr || mt_get_type(descr[0].mt) == lEndT) {
      LERROR(LEDESCRNULL);
      DRETURN(NULL);
   }

   if (!(lp = (lList *) malloc(sizeof(lList)))) {
      LERROR(LEMALLOC);
      DRETURN(NULL);
   }
   if (!(lp->listname = strdup(listname))) {
      FREE(lp);
      LERROR(LESTRDUP);
      DRETURN(NULL);
   }

   lp->nelem = 0;
   if ((n = lCountDescr(descr)) <= 0) {
      FREE(lp->listname);
      FREE(lp);
      LERROR(LECOUNTDESCR);
      DRETURN(NULL);
   }

   lp->first = NULL;
   lp->last = NULL;
   if (!(lp->descr = (lDescr *) malloc(sizeof(lDescr) * (n + 1)))) {
      FREE(lp->listname);
      FREE(lp);
      LERROR(LEMALLOC);
      DRETURN(NULL);
   }
   /* copy descriptor array */
   for (i = 0; i <= n; i++) {
      lp->descr[i].mt = descr[i].mt;
      lp->descr[i].nm = descr[i].nm;

      /* create hashtable if necessary */
      if(hash && mt_do_hashing(lp->descr[i].mt)) {
         lp->descr[i].ht = cull_hash_create(&descr[i], 0);
      } else {
         lp->descr[i].ht = NULL;
      }
      lp->descr[i].mt |= (descr[i].mt & CULL_IS_REDUCED);
   }

   lp->changed = false;

   DRETURN(lp);
}

/****** cull/list/lCreateElemList() *******************************************
*  NAME
*     lCreateElemList() -- Create a list with n elements 
*
*  SYNOPSIS
*     lList* lCreateElemList(const char *listname, const lDescr *descr, 
*                            int nr_elem) 
*
*  FUNCTION
*     Create a list with a given descriptor and insert 'nr_elem' 
*     only initialized elements 
*
*  INPUTS
*     const char *listname - list name
*     const lDescr *descr  - descriptor 
*     int nr_elem          - number of elements 
*
*  RESULT
*     lList* - list or NULL
*******************************************************************************/
lList *lCreateElemList(const char *listname, const lDescr *descr, int nr_elem) 
{
   lList *lp = NULL;
   lListElem *ep = NULL;
   int i;

   DENTER(CULL_LAYER, "lCreateElemList");

   if (!(lp = lCreateList(listname, descr))) {
      LERROR(LECREATELIST);
      DRETURN(NULL);
   }

   for (i = 0; i < nr_elem; i++) {
      if (!(ep = lCreateElem(descr))) {
         LERROR(LECREATEELEM);
         lFreeList(&lp);
         DRETURN(NULL);
      }
      lAppendElem(lp, ep);
   }

   DRETURN(lp);
}

/****** cull/list/lFreeElem() *************************************************
*  NAME
*     lFreeElem() -- Free a element including strings and sublists 
*
*  SYNOPSIS
*     void lFreeElem(lListElem **ep) 
*
*  FUNCTION
*     Free a element including strings and sublists 
*
*  INPUTS
*     lListElem **ep - element, will be set to NULL 
*
*  NOTES
*     MT-NOTE: lRemoveElem() is MT safe
******************************************************************************/
void lFreeElem(lListElem **ep1) 
{
   int i = 0;
   lListElem *ep = NULL;
   
   DENTER(CULL_LAYER, "lFreeElem");

   if (ep1 == NULL || *ep1 == NULL) {
      DRETURN_VOID;
   }

   ep = *ep1;
   
   if (ep->descr == NULL) {
      LERROR(LEDESCRNULL);
      DPRINTF(("NULL descriptor not allowed !!!\n"));
      abort();
   }

   for (i = 0; mt_get_type(ep->descr[i].mt) != lEndT; i++) {
      /* remove element from hash tables */
      if(ep->descr[i].ht != NULL) {
         cull_hash_remove(ep, i);
      }
      
      switch (mt_get_type(ep->descr[i].mt)) {

      case lIntT:
      case lUlongT:
      case lFloatT:
      case lDoubleT:
      case lLongT:
      case lCharT:
      case lBoolT:
      case lRefT:
         break;

      case lStringT:
         if (ep->cont[i].str != NULL) {
            free(ep->cont[i].str);
         }   
         break;

      case lHostT:
         if (ep->cont[i].host != NULL) {
            free(ep->cont[i].host);
         }   
         break;

      case lListT:
         if (ep->cont[i].glp != NULL) {
            lFreeList(&(ep->cont[i].glp));
         }   
         break;

      case lObjectT:
         if (ep->cont[i].obj != NULL) {
            lFreeElem(&(ep->cont[i].obj));
         }   
         break;

      default:
         unknownType("lFreeElem");
         break;
      }
   }

   /* lFreeElem is not responsible for list descriptor array */
   if(ep->status == FREE_ELEM || ep->status == OBJECT_ELEM) {
      cull_hash_free_descr(ep->descr); 
      free(ep->descr);
   }   

   if(ep->cont != NULL) {
      free(ep->cont);
   }   

   sge_bitfield_free_data(&(ep->changed));

   FREE(*ep1);
   DRETURN_VOID;
}

/****** cull/list/lFreeList() *************************************************
*  NAME
*     lFreeList() -- Frees a list including all elements  
*
*  SYNOPSIS
*     void lFreeList(lList **lp) 
*
*  FUNCTION
*     Frees a list including all elements 
*
*  INPUTS
*     lList **lp - list 
*
*  RESULT
*     void
*
*  NOTES
*     MT-NOTE: lFreeList() is MT safe
******************************************************************************/
void lFreeList(lList **lp)
{
   DENTER(CULL_LAYER, "lFreeList");

   if (lp == NULL || *lp == NULL) {
      DRETURN_VOID;
   }

   /* 
    * remove all hash tables, 
    * it is more efficient than removing it at the end 
    */
   if ((*lp)->descr != NULL) {
      cull_hash_free_descr((*lp)->descr);
   }   

   while ((*lp)->first) {
      lListElem *elem = (*lp)->first;
      lRemoveElem(*lp, &elem);
   }   

   if ((*lp)->descr) {
      free((*lp)->descr);
   }   

   if ((*lp)->listname) {
      free((*lp)->listname);
   }

   free(*lp);
   *lp = NULL;

   DRETURN_VOID;
}


/****** cull/list/lAddSubList() ************************************************
*  NAME
*     lAddSubList() -- Append a list to the sublist of an element
*
*  SYNOPSIS
*     int lAddSubList(lListElem *ep, int nm, const lList *to_add) 
*
*  FUNCTION
*     Appends the list 'to_add' to the sublist 'nm' of the element 
*     'ep'. The list pointer becomes invalid and the returned pointer
*     should be used instead to access the complete sublist.
*
*  INPUTS
*     lListElem *ep       - The CULL list element
*     int nm              - The CULL field name of a sublist 
*     const lList *to_add - The list to be added
*
*  RESULT
*     lList * - Returns 
*
*  NOTES
*     MT-NOTE: lAddSubList() is MT safe
*******************************************************************************/
lList *lAddSubList(lListElem *ep, int nm, lList *to_add)
{
   lList *tmp;
   if (lGetNumberOfElem(to_add)) {
      if ((tmp=lGetList(ep, nm)))
         lAddList(tmp, &to_add);
      else 
         lSetList(ep, nm, to_add);
   }
   return lGetList(ep, nm);
}

/****** cull/list/lAddList() **************************************************
*  NAME
*     lAddList() -- Concatenate two lists 
*
*  SYNOPSIS
*     int lAddList(lList *lp0, lList **lp1) 
*
*  FUNCTION
*     Concatenate two lists of equal type throwing away the second list 
*
*  INPUTS
*     lList *lp0 - first list 
*     lList **lp1 - second list 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*
*  NOTES
*     MT-NOTE: lAddList() is MT safe
******************************************************************************/
int lAddList(lList *lp0, lList **lp1) 
{
   /* No need to do any safety checks.  lAppendList will do them for us. */
   int res = 0;
   
   DENTER(CULL_LAYER, "lAddList");
   res = lAppendList(lp0, *lp1);
   lFreeList(lp1);
   DRETURN(res);
}

/****** cull/list/lAppendList() ************************************************
*  NAME
*     lAppendList() -- Concatenate two lists 
*
*  SYNOPSIS
*     int lAppendList(lList *lp0, lList *lp1) 
*
*  FUNCTION
*     Concatenate two lists of equal type without throwing away the second list 
*
*  INPUTS
*     lList *lp0 - first list 
*     lList *lp1 - second list 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*
*  NOTES
*     MT-NOTE: lAppendList() is MT safe
******************************************************************************/
int lAppendList(lList *lp0, lList *lp1) 
{
   lListElem *ep;
   const lDescr *dp0, *dp1;

   DENTER(CULL_LAYER, "lAppendList");

   if (!lp1 || !lp0) {
      LERROR(LELISTNULL);
      DRETURN(-1);
   }

   /* Check if the two lists are equal */
   dp0 = lGetListDescr(lp0);
   dp1 = lGetListDescr(lp1);
   if (lCompListDescr(dp0, dp1)) {
      LERROR(LEDIFFDESCR);
      DRETURN(-1);
   }

   while (lp1->first) {
      if (!(ep = lDechainElem(lp1, lp1->first))) {
         LERROR(LEDECHAINELEM);
         DRETURN(-1);
      }
      if (lAppendElem(lp0, ep) == -1) {
         LERROR(LEAPPENDELEM);
         DRETURN(-1);
      }
   }

   DRETURN(0);
}

/****** cull/list/lOverrideStrList() ************************************************
*  NAME
*     lOverrideStrList() -- Merge two lists 
*
*  SYNOPSIS
*     int lOverrideStrList(lList *lp0, lList *lp1, int nm, const char *str)
*
*  FUNCTION
*     Merge two lists of equal type, and replace values in the first list
*     with values from the second list.
*
*     This only applies to values equal str.
*
*  INPUTS
*     lList *lp0      - first list 
*     lList *lp1      - second list 
*     int nm          - field name used for merging
*     const char *str - override criteria, e.g. "-q" for "override all -q switches,
*                       all others are simply merged"
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*
*  NOTES
*     MT-NOTE: lOverrideStrList() is MT safe
******************************************************************************/
int lOverrideStrList(lList *lp0, lList *lp1, int nm, const char *str)
{
   lListElem *ep;
   const lDescr *dp0, *dp1;
   bool overridden = false;

   DENTER(CULL_LAYER, "lOverrideStrList");

   if (!lp1 || !lp0) {
      LERROR(LELISTNULL);
      DRETURN(-1);
   }

   /* Check if the two lists are equal */
   dp0 = lGetListDescr(lp0);
   dp1 = lGetListDescr(lp1);
   if (lCompListDescr(dp0, dp1)) {
      LERROR(LEDIFFDESCR);
      DRETURN(-1);
   }

   while (lp1->first) {
      if (!(ep = lDechainElem(lp1, lp1->first))) {
         LERROR(LEDECHAINELEM);
         DRETURN(-1);
      }

      /* 
       * if we find elements to override, override them
       * override means:
       * remove all occurencies of the str in lp0
       */
      if (sge_strnullcmp(lGetString(ep, nm), str) == 0 && !overridden) {
         lListElem *tmp;
         while ((tmp = lGetElemStr(lp0, nm, str)) != NULL) {
            lRemoveElem(lp0, &tmp);
         }
         overridden = true;
      }

      /* now copy the elem */
      lAppendElem(lp0, ep);
   }

   lFreeList(&lp1);
   DRETURN(0);
}

/****** cull/list/lCompListDescr() ********************************************
*  NAME
*     lCompListDescr() -- Compare two descriptors 
*
*  SYNOPSIS
*     int lCompListDescr(const lDescr *dp0, const lDescr *dp1) 
*
*  FUNCTION
*     Compare two descriptors 
*
*  INPUTS
*     const lDescr *dp0 - descriptor one 
*     const lDescr *dp1 - descriptor two 
*
*  RESULT
*     int - Result of compare operation
*         0 - equivalent
*        -1 - not equivalent
*
*  NOTES
*     MT-NOTE: lCompListDescr() is MT safe
******************************************************************************/
int lCompListDescr(const lDescr *dp0, const lDescr *dp1) 
{
   int i, n, m;

   DENTER(CULL_LAYER, "lCompListDescr");

   if (!dp0 || !dp1) {
      LERROR(LELISTNULL);
      DRETURN(-1);
   }

   /* Check if the two lists are equal */
   if ((n = lCountDescr(dp0)) <= 0) {
      LERROR(LECOUNTDESCR);
      DRETURN(-1);
   }
   if ((m = lCountDescr(dp1)) <= 0) {
      LERROR(LECOUNTDESCR);
      DRETURN(-1);
   }
   if (n == m) {
      for (i = 0; i < n; i++) {
#if 1          
         int type0 = mt_get_type(dp0[i].mt);
         int type1 = mt_get_type(dp1[i].mt);
         if (dp0[i].nm != dp1[i].nm || type0 != type1) {
#else
         /* 
          * comparing the mt field might be too restrictive
          * former implementation mt only contained the type information 
          * now also the various flags 
          */
         if (dp0[i].nm != dp1[i].nm || dp0[i].mt != dp1[i].mt) {
#endif
            LERROR(LEDIFFDESCR);
            DRETURN(-1);
         }
      }
   }
   else {
      LERROR(LEDIFFDESCR);
      DRETURN(-1);
   }

   DRETURN(0);
}

/****** cull/list/lCopyList() *************************************************
*  NAME
*     lCopyList() -- Copy a list including strings and sublists 
*
*  SYNOPSIS
*     lList* lCopyList(const char *name, const lList *src) 
*
*  FUNCTION
*     Copy a list including strings and sublists. The new list will
*     get 'name' as user defined name 
*
*  INPUTS
*     const char *name - list name 
*     const lList *src - source list 
*
*  RESULT
*     lList* - Copy of 'src' or NULL 
******************************************************************************/
lList *lCopyList(const char *name, const lList *src) {
   return lCopyListHash(name, src, true);
}

/****** cull/list/lCopyListHash() *************************************************
*  NAME
*     lCopyListHash() -- Copy a list including strings and sublists 
*
*  SYNOPSIS
*     lList* lCopyListHash(const char *name, const lList *src, bool isHash) 
*
*  FUNCTION
*     Copy a list including strings and sublists. The new list will
*     get 'name' as user defined name 
*
*  INPUTS
*     const char *name - list name 
*     const lList *src - source list 
*     bool hash - if set to true, a hash table is generated
*
*  RESULT
*     lList* - Copy of 'src' or NULL 
******************************************************************************/
lList *lCopyListHash(const char *name, const lList *src, bool hash) 
{
   lList *dst = NULL;
   lListElem *sep;

   DENTER(CULL_LAYER, "lCopyListHash");

   if (!src) {
      LERROR(LELISTNULL);
      DRETURN(NULL);
   }

   if (!name)
      name = src->listname;

   if (!name)
      name = "No list name specified";


   /* create new list without hashes - we'll hash it once it is filled */
   if (!(dst = lCreateListHash(name, src->descr, false))) {
      LERROR(LECREATELIST);
      DRETURN(NULL);
   }

#if 0 /* TODO EB:  need to decide if we want this or not*/
   dst->changed = src->changed;
#endif

   for (sep = src->first; sep; sep = sep->next) {
      if (lAppendElem(dst, lCopyElem(sep)) == -1) {
         lFreeList(&dst);
         LERROR(LEAPPENDELEM);
         DRETURN(NULL);
      }
   }
   if(hash) {
      /* now create the hash tables */
      cull_hash_create_hashtables(dst);
   }
   
   DRETURN(dst);
}

/****** cull/list/lInsertElem() ***********************************************
*  NAME
*     lInsertElem() -- Insert element after another in a list 
*
*  SYNOPSIS
*     int lInsertElem(lList *lp, lListElem *ep, lListElem *new) 
*
*  FUNCTION
*     Insert a 'new' element after element 'ep' into list 'lp'.
*     If 'ep' is NULL then 'new' will be the first element in 'lp'.
*
*  INPUTS
*     lList *lp      - list 
*     lListElem *ep  - element 
*     lListElem *new - new element 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
******************************************************************************/
int lInsertElem(lList *lp, lListElem *ep, lListElem *new) 
{
   DENTER(CULL_LAYER, "lInsertElem");

   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN(-1);
   }
   
   if (!new) {
      LERROR(LEELEMNULL);
      DRETURN(-1);
   }
   
   /* is the element new still chained in an other list, this is not allowed ? */
   if (new->status == BOUND_ELEM || new->status == OBJECT_ELEM) {
      DPRINTF(("WARNING: tried to insert chained element\n"));
      lWriteElem(new);
      DEXIT;
      abort();
   }

   if (ep) {
      new->prev = ep;
      new->next = ep->next;
      ep->next = new;
      if (new->next)            /* the new element has successors */
         new->next->prev = new;
      else                      /* the new element is the last element */
         lp->last = new;
   }
   else {                       /* insert as first element */
      new->prev = NULL;
      new->next = lp->first;
      if (!lp->first)           /* empty list ? */
         lp->last = new;
      else
         lp->first->prev = new;
      lp->first = new;
   }

   if (new->status == FREE_ELEM) {
      cull_hash_free_descr(new->descr);
      free(new->descr);
   }   
   new->status = BOUND_ELEM;
   new->descr = lp->descr;

   cull_hash_elem(new);
   
   lp->nelem++;
   lp->changed = true;

   DRETURN(0);
}

/****** cull/list/lAppendElem() ***********************************************
*  NAME
*     lAppendElem() -- Append element at the end of a list 
*
*  SYNOPSIS
*     int lAppendElem(lList *lp, lListElem *ep) 
*
*  FUNCTION
*     Append element 'ep' at the end of list 'lp' 
*
*  INPUTS
*     lList *lp     - list 
*     lListElem *ep - element 
*
*  RESULT
*     int - error state 
*         0 - OK
*        -1 - Error
*
*  NOTES
*     MT-NOTE: lAppendElem() is MT safe
******************************************************************************/
int lAppendElem(lList *lp, lListElem *ep) 
{
   DENTER(CULL_LAYER, "lAppendElem");

   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN(-1);
   }
   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN(-1);
   }

   /* is the element ep still chained in an other list, this is not allowed ? */
   if (ep->status == BOUND_ELEM || ep->status == OBJECT_ELEM) {
      DPRINTF(("WARNING: tried to append chained element\n"));
      abort();
   }

   if (lp->last) {
      lp->last->next = ep;
      ep->prev = lp->last;
      lp->last = ep;
      ep->next = NULL;
   }
   else {
      lp->last = lp->first = ep;
      ep->prev = ep->next = NULL;
   }

   if (ep->status == FREE_ELEM) {
      cull_hash_free_descr(ep->descr);
      free(ep->descr);
   }
   ep->status = BOUND_ELEM;
   ep->descr = lp->descr;

   cull_hash_elem(ep);
   lp->nelem++;
   lp->changed = true;

   DRETURN(0);
}

/****** cull/list/lRemoveElem() ***********************************************
*  NAME
*     lRemoveElem() -- Delete a element from a list 
*
*  SYNOPSIS
*     int lRemoveElem(lList *lp, lListElem *ep) 
*
*  FUNCTION
*     Remove element 'ep' from list 'lp'. 'ep' gets deleted.
*
*  INPUTS
*     lList *lp     - list 
*     lListElem **ep - element, will be set to NULL 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error 
*
*  NOTES
*     MT-NOTE: lRemoveElem() is MT safe
*******************************************************************************/
int lRemoveElem(lList *lp, lListElem **ep1) 
{
   lListElem *ep = NULL;
   DENTER(CULL_LAYER, "lRemoveElem");

   if (lp == NULL || ep1 == NULL || *ep1 == NULL) {
      DRETURN(-1);
   }

   ep = *ep1;
   
   if (lp->descr != ep->descr) {
      CRITICAL((SGE_EVENT, "Removing element from other list !!!\n"));
      DEXIT;
      abort();
   }

   if (ep->prev) {
      ep->prev->next = ep->next;
   }
   else {
      lp->first = ep->next;
   }

   if (ep->next) {
      ep->next->prev = ep->prev;
   }
   else {
      lp->last = ep->prev;
   }

   /* NULL the ep next and previous pointers */
   ep->prev = ep->next = NULL;

   lp->nelem--;
   lp->changed = true;

   lFreeElem(ep1);
   DRETURN(0);
}

/****** cull/list/lDechainList() **********************************************
*  NAME
*     lDechainList() -- splits a list into two at the given elem.
*
*  SYNOPSIS
*     lListElem* lDechainList(lList *lp, lListElem *ep) 
*
*  FUNCTION
*    splits a list into two at the given elem.
*    If no target list is given, new one is created, otherwise the splited
*    list is appended to the second one.
*
*  INPUTS
*     lList *source  - list 
*     lList **target - list
*     lListElem *ep  - element 
*
*
*  NOTES
*     MT-NOTE: lDechainList() is MT safe
******************************************************************************/
void 
lDechainList(lList *source, lList **target, lListElem *ep)
{
   lListElem *target_last;

   DENTER(CULL_LAYER, "lDechainList");

   if (source == NULL || target == NULL) {
      LERROR(LELISTNULL);
      DRETURN_VOID;
   }
   if (ep == NULL) {
      LERROR(LEELEMNULL);
      DRETURN_VOID;
   }

   if (source->descr != ep->descr) {
      CRITICAL((SGE_EVENT,"Dechaining element from other list !!!\n"));
      DEXIT;
      abort();
   }
  
   if (*target == NULL) {
      *target = lCreateList(lGetListName(source), source->descr);
   } else {
      if (lCompListDescr(source->descr, (*target)->descr) != 0) {
         CRITICAL((SGE_EVENT,"Dechaining element into a different list !!!\n"));
         DEXIT;
         abort();
         
      }
   }
   
   cull_hash_free_descr(source->descr);
   cull_hash_free_descr((*target)->descr);

   target_last = source->last;
 
   if (ep->prev != NULL) {
      ep->prev->next = NULL;
      source->last = ep->prev;
   }
   else {
      source->first = NULL;
      source->last = NULL;
   }

   if ((*target)->first != NULL) {
      (*target)->last->next = ep;
      ep->prev = (*target)->last; 
   }
   else {
      ep->prev = NULL;
      (*target)->first = ep;
   }
   (*target)->last = target_last;

   for (;ep != NULL; ep = ep->next) {
      ep->descr = (*target)->descr;
      (*target)->nelem++;
      source->nelem--;
   }
 
   source->changed = true;
   (*target)->changed = true;
 
   cull_hash_create_hashtables(source);
   cull_hash_create_hashtables(*target);
   
   
   DRETURN_VOID; 
}

/****** cull/list/lDechainElem() **********************************************
*  NAME
*     lDechainElem() -- Remove a element from a list 
*
*  SYNOPSIS
*     lListElem* lDechainElem(lList *lp, lListElem *ep) 
*
*  FUNCTION
*     Remove element 'ep' from list 'lp'. 'ep' gets not deleted. 
*
*  INPUTS
*     lList *lp     - list 
*     lListElem *ep - element 
*
*  RESULT
*     lListElem* - dechained element or NULL
*
*  NOTES
*     MT-NOTE: lDechainElem() is MT safe
******************************************************************************/
lListElem *lDechainElem(lList *lp, lListElem *ep) 
{
   int i;

   DENTER(CULL_LAYER, "lDechainElem");

   if (!lp) {
      LERROR(LELISTNULL);
      DRETURN(NULL);
   }
   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN(NULL);
   }
   if (lp->descr != ep->descr) {
      CRITICAL((SGE_EVENT,"Dechaining element from other list !!!"));
      DEXIT;
      abort();
   }

   if (ep->prev) {
      ep->prev->next = ep->next;
   }   
   else {
      lp->first = ep->next;
   }   

   if (ep->next) {
      ep->next->prev = ep->prev;
   }   
   else {
      lp->last = ep->prev;
   }   

   /* remove hash entries */
   for(i = 0; mt_get_type(ep->descr[i].mt) != lEndT; i++) {
      if(ep->descr[i].ht != NULL) {
         cull_hash_remove(ep, i);
      }
   }

   /* NULL the ep next and previous pointers */
   ep->prev = ep->next = (lListElem *) NULL;
   ep->descr = lCopyDescr(ep->descr);
   ep->status = FREE_ELEM;
   lp->nelem--;
   lp->changed = true;

   DRETURN(ep);
}

/****** cull/list/lDechainObject() **********************************************
*  NAME
*     lDechainObject() -- Remove a element from a list 
*
*  SYNOPSIS
*     lListElem* lDechainObject(lList *lp, int name) 
*
*  FUNCTION
*     Remove element 'ep' from list 'lp'. 'ep' gets not deleted. 
*
*  INPUTS
*     lList *lp     - list 
*     int name      - attribute name 
*
*  RESULT
*     lListElem* - dechained element or NULL
******************************************************************************/
lListElem *lDechainObject(lListElem *parent, int name) 
{
   int pos;
   lListElem *dep;

   DENTER(CULL_LAYER, "lDechainObject");

   if (parent == NULL) {
      LERROR(LEELEMNULL);
      DRETURN(NULL);
   }

   pos = lGetPosViaElem(parent, name, SGE_DO_ABORT);

   if(mt_get_type(parent->descr[pos].mt) != lObjectT) {
      incompatibleType2(MSG_CULL_DECHAINOBJECT_WRONGTYPEFORFIELDXY_S,
                        lNm2Str(name));
      DEXIT;
   }
  
   dep = (lListElem *) parent->cont[pos].obj;

   if(dep != NULL) {
      dep->status = FREE_ELEM;
      parent->cont[pos].obj = NULL;

      /* remember that field changed */
      sge_bitfield_set(&(parent->changed), pos);
   }

   DRETURN(dep);
}

/****** cull/list/lFirst() ****************************************************
*  NAME
*     lFirst() -- Return the first element of a list 
*
*  SYNOPSIS
*     lListElem* lFirst(const lList *slp) 
*
*  FUNCTION
*     Return the first element of a list. 
*
*  INPUTS
*     const lList *slp - list 
*
*  RESULT
*     lListElem* - first element or NULL 
******************************************************************************/
lListElem *lFirst(const lList *slp) 
{
   DENTER(CULL_LAYER, "lFirst");
   DRETURN(slp ? slp->first : NULL);
}

/****** cull/list/lLast() *****************************************************
*  NAME
*     lLast() -- Returns the last element of a list 
*
*  SYNOPSIS
*     lListElem* lLast(const lList *slp) 
*
*  FUNCTION
*     Returns the last element of a list. 
*
*  INPUTS
*     const lList *slp - list 
*
*  RESULT
*     lListElem* - last element or NULL 
******************************************************************************/
lListElem *lLast(const lList *slp) 
{
   DENTER(CULL_LAYER, "lLast");
   DRETURN(slp ? slp->last : NULL);
}

/****** cull/list/lNext() *****************************************************
*  NAME
*     lNext() -- Returns the next element or NULL
*
*  SYNOPSIS
*     lListElem* lNext(const lListElem *sep) 
*
*  FUNCTION
*     Returns the next element of 'sep' or NULL 
*
*  INPUTS
*     const lListElem *sep - element 
*
*  RESULT
*     lListElem* - next element or NULL 
*******************************************************************************/
lListElem *lNext(const lListElem *sep) 
{
   DENTER(CULL_LAYER, "lNext");
   DRETURN(sep ? sep->next : NULL);
}

/****** cull/list/lPrev() *****************************************************
*  NAME
*     lPrev() -- Returns the previous element or NULL 
*
*  SYNOPSIS
*     lListElem* lPrev(const lListElem *sep) 
*
*  FUNCTION
*     Returns the previous element or NULL. 
*
*  INPUTS
*     const lListElem *sep - element 
*
*  RESULT
*     lListElem* - previous element 
******************************************************************************/
lListElem *lPrev(const lListElem *sep) 
{
   DENTER(CULL_LAYER, "lPrev");
   DRETURN(sep ? sep->prev : NULL);
}

/****** cull/list/lFindFirst() ************************************************
*  NAME
*     lFindFirst() -- Returns first element fulfilling condition 
*
*  SYNOPSIS
*     lListElem* lFindFirst(const lList *slp, const lCondition *cp) 
*
*  FUNCTION
*     Returns the first element fulfilling the condition 'cp' or
*     NULL if nothing is found. If the condition is NULL the first
*     element is delivered. 
*
*  INPUTS
*     const lList *slp     - list 
*     const lCondition *cp - condition 
*
*  RESULT
*     lListElem* - element or NULL 
******************************************************************************/
lListElem *lFindFirst(const lList *slp, const lCondition *cp) 
{
   lListElem *ep;

   DENTER(CULL_LAYER, "lFindFirst");

   if (!slp) {
      LERROR(LELISTNULL);
      DRETURN(NULL);
   }

   /* ep->next=NULL for the last element */
   for (ep = slp->first; ep && !lCompare(ep, cp); ep = ep->next);

   DRETURN(ep);
}

/****** cull/list/lFindLast() *************************************************
*  NAME
*     lFindLast() -- Returns last element fulfilling condition 
*
*  SYNOPSIS
*     lListElem* lFindLast(const lList *slp, const lCondition *cp) 
*
*  FUNCTION
*     Retruns the last element fulfilling the condition 'cp' or NULL
*     if nothing is found. If the condition is NULL then the last
*     element is delivered.
*
*  INPUTS
*     const lList *slp     - list 
*     const lCondition *cp - condition 
*
*  RESULT
*     lListElem* - element or NULL
******************************************************************************/
lListElem *lFindLast(const lList *slp, const lCondition *cp) 
{
   lListElem *ep;

   DENTER(CULL_LAYER, "lFindLast");

   if (!slp) {
      LERROR(LELISTNULL);
      DRETURN(NULL);
   }

   /* ep->prev=NULL for the first element */
   for (ep = slp->last; ep && !lCompare(ep, cp); ep = ep->prev);

   DRETURN(ep);
}

/****** cull/list/lFindNext() *************************************************
*  NAME
*     lFindNext() -- Returns the next element fulfilling condition 
*
*  SYNOPSIS
*     lListElem* lFindNext(const lListElem *ep, const lCondition *cp) 
*
*  FUNCTION
*     Returns the next element fulfilling the condition 'cp' or NULL
*     if nothing is found. If condition is NULL than the following
*     element is delivered. 
*
*  INPUTS
*     const lListElem *ep  - element 
*     const lCondition *cp - condition 
*
*  RESULT
*     lListElem* - element or NULL 
*******************************************************************************/
lListElem *lFindNext(const lListElem *ep, const lCondition *cp) 
{
   DENTER(CULL_LAYER, "lFindNext");

   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN(NULL);
   }

   do {
      ep = ep->next;
   } while (ep && (lCompare(ep, cp) == 0));

   DRETURN((lListElem *)ep);
}

/****** cull/list/lFindPrev() *************************************************
*  NAME
*     lFindPrev() -- Returns previous element fulfilling condition 
*
*  SYNOPSIS
*     lListElem* lFindPrev(const lListElem *ep, const lCondition *cp) 
*
*  FUNCTION
*     Returns the previous element fulfilling the condition 'cp' or
*     NULL if nothing is found. If condition is NULL than the following
*     element is delivered. 
*
*  INPUTS
*     const lListElem *ep  - element 
*     const lCondition *cp - condition 
*
*  RESULT
*     lListElem* - element or NULL 
******************************************************************************/
lListElem *lFindPrev(const lListElem *ep, const lCondition *cp) 
{
   DENTER(CULL_LAYER, "lFindNext");

   if (!ep) {
      LERROR(LEELEMNULL);
      DRETURN(NULL);
   }

   do {
      ep = ep->prev;
   } while (ep && (lCompare(ep, cp) == 0));

   DRETURN((lListElem *) ep);
}

/****** cull/list/lPSortList() ************************************************
*  NAME
*     lPSortList() -- Sort a given list 
*
*  SYNOPSIS
*     int lPSortList(lList * lp, const char *fmt, ...) 
*
*  FUNCTION
*     Sort a given list. The sorting order is given by the format 
*     string and additional arguments. 
*
*  INPUTS
*     lList * lp      - list 
*     const char *fmt - format string (see lParseSortOrder()) 
*     ...             - additional arguments (see lParseSortOrder()) 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*
*  SEE ALSO
*     cull/list/lParseSortOrder() 
******************************************************************************/
int lPSortList(lList * lp, const char *fmt,...)
{
   va_list ap;

   lSortOrder *sp;

   DENTER(CULL_LAYER, "lPSortList");

   va_start(ap, fmt);
   if (!lp || !fmt) {
      LERROR(LELISTNULL);
      va_end(ap);
      DRETURN(-1);
   }
   if (!(sp = lParseSortOrder(lp->descr, fmt, ap))) {
      LERROR(LEPARSESORTORD);
      va_end(ap);
      DRETURN(-1);
   }

   lSortList(lp, sp);

   va_end(ap);
   lFreeSortOrder(&sp);

   DRETURN(0);
}

/****** cull/list/lSortList() *************************************************
*  NAME
*     lSortList() -- Sort list according to sort order object 
*
*  SYNOPSIS
*     int lSortList(lList *lp, const lSortOrder *sp) 
*
*  FUNCTION
*     Sort list according to sort order object. 
*
*  INPUTS
*     lList *lp            - list 
*     const lSortOrder *sp - sort order object 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
******************************************************************************/
int lSortList(lList *lp, const lSortOrder *sp) 
{
   lListElem *ep;
   lListElem **pointer;
   int i, n;

   DENTER(CULL_LAYER, "lSortList");

   if (!lp) {
      DRETURN(0);                 /* ok list is sorted */
   }

   /* 
    * step 1: build up a pointer array for use of qsort
    */

   n = lGetNumberOfElem(lp);
   if (n < 2) {
      DRETURN(0);                 /* ok list is sorted */
   }

   if (!(pointer = (lListElem **) malloc(sizeof(lListElem *) * n))) {
      DRETURN(-1);                /* low memory */
   }

#ifdef RANDOMIZE_QSORT_ELEMENTS

   for (i = 0, ep = lFirst(lp); ep; i++, ep = lNext(ep)) {
      int j = (int)((double)i*rand()/(RAND_MAX+1.0));
      pointer[i] = pointer[j];
      pointer[j] = ep;
   }

#else

   for (i = 0, ep = lFirst(lp); ep; i++, ep = lNext(ep))
      pointer[i] = ep;

#endif

   /* 
    * step 2: sort the pointer array using parsed sort order 
    */
   cull_state_set_global_sort_order(sp);
   /* this is done to pass the sort order */
   /* to the lSortCompare function called */
   /* by lSortCompareUsingGlobal          */
   qsort((void *) pointer, n, sizeof(lListElem *), lSortCompareUsingGlobal);

   /* 
    * step 3: relink elements in list according pointer array
    */
   lp->first = pointer[0];
   lp->last = pointer[n - 1];

   /* handle first element separatly */
   pointer[0]->prev = NULL;
   pointer[n - 1]->next = NULL;

   if (n > 1) {
      pointer[0]->next = pointer[1];
      pointer[n - 1]->prev = pointer[n - 2];
   }

   for (i = 1; i < n - 1; i++) {
      pointer[i]->prev = pointer[i - 1];
      pointer[i]->next = pointer[i + 1];
   }

   free(pointer);

   cull_hash_recreate_after_sort(lp);

   DRETURN(0);
}

/****** cull/list/lUniqStr() **************************************************
*  NAME
*     lUniqStr() -- Uniq a string key list 
*
*  SYNOPSIS
*     int lUniqStr(lList *lp, int keyfield) 
*
*  FUNCTION
*     Uniq a string key list 
*
*  INPUTS
*     lList *lp    - list 
*     int keyfield - string field name id 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
******************************************************************************/
int lUniqStr(lList *lp, int keyfield) 
{
   lListElem *ep;
   lListElem *rep;

   DENTER(CULL_LAYER, "lUniqStr");

   /*
    * sort the list first to make our algorithm work
    */
   if (lPSortList(lp, "%I+", keyfield)) {
      DRETURN(-1);
   }

   /*
    * go over all elements and remove following elements
    */
   ep = lFirst(lp);
   while (ep) {
      rep = lNext(ep);
      while (rep &&
          !strcmp(lGetString(rep, keyfield), lGetString(ep, keyfield))) {
         lRemoveElem(lp, &rep);
         rep = lNext(ep);
      }
      ep = lNext(ep);
   }

   DRETURN(0);
}

/****** cull/list/lUniqHost() *************************************************
*  NAME
*     lUniqHost() -- Uniq a host key list 
*
*  SYNOPSIS
*     int lUniqHost(lList *lp, int keyfield) 
*
*  FUNCTION
*     Uniq a hostname key list. 
*
*  INPUTS
*     lList *lp    - list 
*     int keyfield - host field 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
******************************************************************************/
int lUniqHost(lList *lp, int keyfield) 
{
   lListElem *ep;
   lListElem *rep;

   DENTER(CULL_LAYER, "lUniqHost");

   /*
    * sort the list first to make our algorithm work
    */
   if (lPSortList(lp, "%I+", keyfield)) {
      DRETURN(-1);
   }

   /*
    * go over all elements and remove following elements
    */
   ep = lFirst(lp);
   while (ep) {
      rep = lNext(ep);
      while (rep &&
          !strcmp(lGetHost(rep, keyfield), lGetHost(ep, keyfield))) {
         lRemoveElem(lp, &rep);
         rep = lNext(ep);
      }
      ep = lNext(ep);
   }

   DRETURN(0);
}

/****** cull/list/mt_get_type() ************************************************
*  NAME
*     mt_get_type() -- get data type for cull object field
*
*  SYNOPSIS
*     int mt_get_type(int mt) 
*
*  FUNCTION
*     Returns the data type of a cull object field given the multitype 
*     attribute of a cull descriptor.
*
*  INPUTS
*     int mt - mt (multitype) struct element of a field descriptor
*
*  RESULT
*     int - cull data type enum value (from _enum_lMultiType)
*
*  EXAMPLE
*     switch(mt_get_type(descr[i].mt)) {
*        case lFloatT:
*           ...
*     }
*
*  SEE ALSO
*     
*
*  NOTES
*     MT-NOTE: mt_get_type() is MT safe
*******************************************************************************/
/****** cull/list/mt_do_hashing() ************************************************
*  NAME
*     mt_do_hashing() -- is there hash access for a field
*
*  SYNOPSIS
*     int mt_do_hashing(int mt) 
*
*  FUNCTION
*     Returns the information if hashing is active for a cull object field 
*     given the multitype attribute of a cull descriptor.
*
*  INPUTS
*     int mt - mt (multitype) struct element of a field descriptor
*
*  RESULT
*     int - 1, if hashing is requested, else 0
*
*  SEE ALSO
*     
*******************************************************************************/
/****** cull/list/mt_is_unique() ************************************************
*  NAME
*     mt_is_unique() -- is the cull object field unique 
*
*  SYNOPSIS
*     int mt_is_unique(int mt) 
*
*  FUNCTION
*     Returns the information if a certain cull object field is unique within
*     a cull list given the multitype attribute of a cull descriptor.
*
*  INPUTS
*     int mt - mt (multitype) struct element of a field descriptor
*
*  RESULT
*     int - 1 = unique, 0 = not unique
*
*  EXAMPLE
*     if(mt_is_unique(descr[i].mt)) {
*        // check for uniqueness before inserting new elemente into a list
*        if(lGetElemUlong(....) != NULL) {
*           WARNING((SGE_EVENT, MSG_DUPLICATERECORD....));
*           DRETURN(NULL);
*        }
*     }
*
*  SEE ALSO
*     
*******************************************************************************/
bool 
lListElem_is_pos_changed(const lListElem *ep, int pos)
{
   return sge_bitfield_get(&(ep->changed), pos);
}


bool
lListElem_is_changed(const lListElem *ep)
{
   return sge_bitfield_changed(&(ep->changed));
}

/****** cull_list/lList_clear_changed_info() ***********************************
*  NAME
*     lList_clear_changed_info() -- clear changed info of a list
*
*  SYNOPSIS
*     bool 
*     lList_clear_changed_info(lList *lp) 
*
*  FUNCTION
*     Clears in a list the information, if the list itself has been changed
*     (objects added/removed) and which fields have changed in the lists
*     objects.
*
*  INPUTS
*     lList *lp - the list to update
*
*  RESULT
*     bool - true on success, else false
*
*  NOTES
*
*  BUGS
*
*  SEE ALSO
*     cull_list/lListElem_clear_changed_info()
*******************************************************************************/
bool
lList_clear_changed_info(lList *lp)
{
   bool ret = true;

   if (lp == NULL) {
      ret = false;
   } else {
      lListElem *ep;

      lp->changed = false;

      for_each(ep, lp) {
         lListElem_clear_changed_info(ep);
      }
   }

   return ret;
}

/****** cull_list/lListElem_clear_changed_info() *******************************
*  NAME
*     lListElem_clear_changed_info() -- clear changed info of an object
*
*  SYNOPSIS
*     bool 
*     lListElem_clear_changed_info(lListElem *ep) 
*
*  FUNCTION
*     clears in an object the information, which fields have been changed.
*     Recursively walks down sublists.
*
*  INPUTS
*     lListElem *ep - the list element to update
*
*  RESULT
*     bool - true on success, else false
*
*  NOTES
*
*  BUGS
*
*  SEE ALSO
*     cull_list/lList_clear_changed_info()
*******************************************************************************/
bool 
lListElem_clear_changed_info(lListElem *ep)
{
   bool ret = true;

   if (ep == NULL) {
      ret = false;
   } else {
      int i;
      lDescr *descr = ep->descr;

      for (i = 0; ep->descr[i].nm != NoName; i++) {
         int type = mt_get_type(descr[i].mt);
         
         if (type == lListT) {
            lList_clear_changed_info(ep->cont[i].glp);
         } else if (type == lObjectT) {
            lListElem_clear_changed_info(ep->cont[i].obj);
         }
      }

      /* clear bitfield of this object */
      sge_bitfield_reset(&(ep->changed));
   }

   return ret;
}

