/*___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 <string.h>
#include <float.h>

#include "rmon/sgermon.h"

#include "uti/sge_string.h"
#include "uti/sge_log.h"
#include "uti/sge_parse_num_par.h"

#include "cull/cull_list.h"

#include "comm/commd_message_flags.h"

#include "sgeobj/sge_resource_quota.h"

#include "sched/msg_schedd.h"

#include "sge.h"
#include "sge_answer.h"
#include "sge_schedd_conf.h"
#include "sge_host.h"
#include "sge_cqueue.h"
#include "sge_attr.h"
#include "sge_qinstance.h"
#include "sge_ulong.h"
#include "sge_centry.h"
#include "sge_object.h"
#include "sge_eval_expression.h"
#include "cull_parse_util.h"

#include "msg_common.h"
#include "msg_sgeobjlib.h"

#define CENTRY_LAYER BASIS_LAYER

/* EB: ADOC: add commets */

const int max_host_resources=28;/* specifies the number of elements in the host_resource array */
const struct queue2cmplx host_resource[] = {
   {"arch",           0, 0, 0, TYPE_STR},
   {"cpu",            0, 0, 0, TYPE_DOUBLE},
   {"load_avg",       0, 0, 0, TYPE_DOUBLE},
   {"load_long",      0, 0, 0, TYPE_DOUBLE},
   {"load_medium",    0, 0, 0, TYPE_DOUBLE},
   {"load_short",     0, 0, 0, TYPE_DOUBLE},
   {"mem_free",       0, 0, 0, TYPE_MEM},
   {"mem_total",      0, 0, 0, TYPE_MEM},
   {"mem_used",       0, 0, 0, TYPE_MEM},
   {"min_cpu_inter",  0, 0, 0, TYPE_TIM},
   {"np_load_avg",    0, 0, 0, TYPE_DOUBLE},
   {"np_load_long",   0, 0, 0, TYPE_DOUBLE},
   {"np_load_medium", 0, 0, 0, TYPE_DOUBLE},
   {"np_load_short",  0, 0, 0, TYPE_DOUBLE},
   {"num_proc",       0, 0, 0, TYPE_INT},
   {"swap_free",      0, 0, 0, TYPE_MEM},
   {"swap_rate",      0, 0, 0, TYPE_MEM},
   {"swap_rsvd",      0, 0, 0, TYPE_MEM},
   {"swap_total",     0, 0, 0, TYPE_MEM},
   {"swap_used",      0, 0, 0, TYPE_MEM},
   {"virtual_free",   0, 0, 0, TYPE_MEM},
   {"virtual_total",  0, 0, 0, TYPE_MEM},
   {"virtual_used",   0, 0, 0, TYPE_MEM},
   {"display_win_gui",0, 0, 0, TYPE_BOO},
   {"m_core",         0, 0, 0, TYPE_INT},
   {"m_socket",       0, 0, 0, TYPE_INT},
   {"m_topology",     0, 0, 0, TYPE_STR},
   {"m_topology_inuse",0,0, 0, TYPE_STR}
};

const int max_queue_resources=24; /* specifies the number of elements in the queue_resource array */
const struct queue2cmplx queue_resource[] = {
   {"qname",            QU_qname,            0,                   0,            TYPE_STR },
   {"hostname",         QU_qhostname,        0,                   0,            TYPE_HOST},
   {"slots",            QU_job_slots,        0,                   0,            TYPE_INT },
   {"tmpdir",           QU_tmpdir,           0,                   0,            TYPE_STR }, 
   {"seq_no",           QU_seq_no,           0,                   0,            TYPE_INT },
   {"rerun",            QU_rerun,            0,                   0,            TYPE_BOO },
   {"calendar",         QU_calendar,         CQ_calendar,         ASTR_value,   TYPE_STR }, /* value is SGE_STRING */
   {"s_rt",             QU_s_rt,             CQ_s_rt,             ATIME_value,  TYPE_TIM }, /* value is SGE_STRING */
   {"h_rt",             QU_h_rt,             CQ_h_rt,             ATIME_value,  TYPE_TIM }, /* value is SGE_STRING */
   {"s_cpu",            QU_s_cpu,            CQ_s_cpu,            ATIME_value,  TYPE_TIM }, /* value is SGE_STRING */
   {"h_cpu",            QU_h_cpu,            CQ_h_cpu,            ATIME_value,  TYPE_TIM }, /* value is SGE_STRING */
   {"s_fsize",          QU_s_fsize,          CQ_s_data,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"h_fsize",          QU_h_fsize,          CQ_h_fsize,          AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"s_data",           QU_s_data,           CQ_s_data,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"h_data",           QU_h_data,           CQ_h_data,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"s_stack",          QU_s_stack,          CQ_s_stack,          AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"h_stack",          QU_h_stack,          CQ_h_stack,          AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"s_core",           QU_s_core,           CQ_s_core,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"h_core",           QU_h_core,           CQ_h_core,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"s_rss",            QU_s_rss,            CQ_s_rss,            AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"h_rss",            QU_h_rss,            CQ_h_rss,            AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"s_vmem",           QU_s_vmem,           CQ_s_vmem,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"h_vmem",           QU_h_vmem,           CQ_h_vmem,           AMEM_value,   TYPE_MEM }, /* value is SGE_STRING */
   {"min_cpu_interval", QU_min_cpu_interval, CQ_min_cpu_interval, AINTER_value, TYPE_TIM }  /* value is SGE_STRING */
};

int get_rsrc(const char *name, bool queue, int *field, int *cqfld, int *valfld, int *type)
{
   int pos = 0;
   const struct queue2cmplx *rlist;
   int nitems;

   if (queue) {
      rlist = &queue_resource[0];
      nitems = max_queue_resources;
   } else {
      rlist = &host_resource[0];
      nitems = max_host_resources;
   }

   for (; pos < nitems; pos++) {
      if (strcmp(name, rlist[pos].name) == 0) {
         if (field) *field = rlist[pos].field;
         if (cqfld) *cqfld = rlist[pos].cqfld;
         if (valfld) *valfld = rlist[pos].valfld;
         if (type) *type = rlist[pos].type;
         return 0;
      }
   }

   return -1;
}

/****** sgeobj/centry/centry_fill_and_check() *********************************
*  NAME
*     centry_fill_and_check() -- fill and check the attribute
*
*  SYNOPSIS
*     int centry_fill_and_check(lListElem *cep,
*                               bool allow_empty_boolean,
*                               bool allow_neg_consumable)
*
*  FUNCTION
*     fill and check the attribute
*
*  INPUTS
*     lListElem *cep           - CE_Type, this object will be checked
*     lList** answer_list      - answer list
*     int allow_empty_boolean  - boolean
*        true  - NULL values of boolean attributes will
*                be replaced with "true"
*        false - NULL values will be handled as error
*     int allow_neg_consumable - boolean
*        true  - negative values for consumable
*                resources are allowed.
*        false - function will return with -1 if it finds
*                consumable resources with a negative value
*
*  RESULT
*        0 on success
*       -1 on error
*        an error message will be written into SGE_EVENT
******************************************************************************/
int
centry_fill_and_check(lListElem *this_elem, lList** answer_list, bool allow_empty_boolean,
                      bool allow_neg_consumable)
{
   static char tmp[1000];
   const char *name, *s;
   u_long32 type;
   double dval;
   int ret, allow_infinity;

   DENTER(CENTRY_LAYER, "centry_fill_and_check");

   name = lGetString(this_elem, CE_name);
   s = lGetString(this_elem, CE_stringval);
   /* allow infinity for non-consumables only */
   allow_infinity = (lGetUlong(this_elem, CE_consumable) != CONSUMABLE_NO)?0:1;

   if (!s) {
      if (allow_empty_boolean && lGetUlong(this_elem, CE_valtype)==TYPE_BOO) {
         lSetString(this_elem, CE_stringval, "TRUE");
         s = lGetString(this_elem, CE_stringval);
      } else {
/*          ERROR((SGE_EVENT, MSG_CPLX_VALUEMISSING_S, name)); */
         answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_CPLX_VALUEMISSING_S, name);
         DRETURN(-1);
      }
   }

   switch ( type = lGetUlong(this_elem, CE_valtype) ) {
      case TYPE_INT:
      case TYPE_TIM:
      case TYPE_MEM:
      case TYPE_BOO:
      case TYPE_DOUBLE:
         if (!extended_parse_ulong_val(&dval, NULL, type, s, tmp, sizeof(tmp)-1, allow_infinity, false)) {
/*             ERROR((SGE_EVENT, MSG_CPLX_WRONGTYPE_SSS, name, s, tmp)); */
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_ATTRIB_XISNOTAY_SS, name, tmp);
            DRETURN(-1);
         }
         lSetDouble(this_elem, CE_doubleval, dval);

         /* normalize time values, so that the string value is based on seconds */
         if (type == TYPE_TIM && dval != DBL_MAX) {
            char str_value[100];
            dstring ds;
            sge_dstring_init(&ds, str_value, sizeof(str_value));
            sge_dstring_sprintf(&ds, "%.0f", dval );
            DPRINTF(("normalized time value from \"%s\" to \"%s\"\n",
                     lGetString(this_elem, CE_stringval), str_value));
            lSetString(this_elem, CE_stringval, str_value);
         }
         
         /* also the CE_default must be parsable for numeric types */
         if ((s=lGetString(this_elem, CE_default))
            && !parse_ulong_val(&dval, NULL, type, s, tmp, sizeof(tmp)-1)) {
/*             ERROR((SGE_EVENT, MSG_CPLX_WRONGTYPE_SSS, name, s, tmp)); */
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_CPLX_WRONGTYPE_SSS, name, s, tmp);
            DRETURN(-1);
         }

         /* negative values are not allowed for consumable attributes */
         if (!allow_neg_consumable && (lGetUlong(this_elem, CE_consumable) != CONSUMABLE_NO)
             && lGetDouble(this_elem, CE_doubleval) < (double)0.0) {
/*             ERROR((SGE_EVENT, MSG_CPLX_ATTRIBISNEG_S, name)); */
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_CPLX_ATTRIBISNEG_S, name);

            DRETURN(-1);
         }
         break;
      case TYPE_HOST:
         /* resolve hostname and store it */
         ret = sge_resolve_host(this_elem, CE_stringval);
         if (ret != CL_RETVAL_OK ) {
            if (ret == CL_RETVAL_GETHOSTNAME_ERROR) {
/*                ERROR((SGE_EVENT, MSG_SGETEXT_CANTRESOLVEHOST_S, s)); */
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_SGETEXT_CANTRESOLVEHOST_S, s);
            } else {
/*                ERROR((SGE_EVENT, MSG_SGETEXT_INVALIDHOST_S, s)); */
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_SGETEXT_INVALIDHOST_S, s);
            }
            DRETURN(-1);
         }
         break;
      case TYPE_STR:
      case TYPE_CSTR:
      case TYPE_RESTR:
         /* no restrictions - so everything is ok */
         break;

      default:
/*          ERROR((SGE_EVENT, MSG_SGETEXT_UNKNOWN_ATTR_TYPE_U, sge_u32c(type))); */
         answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR,
                                 MSG_SGETEXT_UNKNOWN_ATTR_TYPE_U, sge_u32c(type));
         DRETURN(-1);
   }

   DRETURN(0);
}

const char *
map_op2str(u_long32 op)
{
   static char *opv[] = {
      "??",
      "==",  /* CMPLXEQ_OP */
      ">=",  /* CMPLXGE_OP */
      ">",   /* CMPLXGT_OP */
      "<",   /* CMPLXLT_OP */
      "<=",  /* CMPLXLE_OP */
      "!=",  /* CMPLXNE_OP */
      "EXCL" /* CMPLXEXCL_OP */
   };

   if (op < CMPLXEQ_OP || op > CMPLXEXCL_OP) {
      op = 0;
   }
   return opv[op];
}

const char *
map_req2str(u_long32 op)
{
   static char *opv[] = {
      "??",
      "NO",       /* REQU_NO */
      "YES",      /* REQU_YES */
      "FORCED",   /* REQU_FORCED */
   };

   if (op < REQU_NO || op > REQU_FORCED) {
      op = 0;
   }
   return opv[op];
}

/****** sge_centry/map_consumable2str() ****************************************
*  NAME
*     map_consumable2str() -- map to consumable string
*
*  SYNOPSIS
*     const char * map_consumable2str(u_long32 op) 
*
*  FUNCTION
*     maps int representation of CONSUMABLE to string
*
*  INPUTS
*     u_long32 op - CONSUMABLE_*
*
*  RESULT
*     const char * - string representation of consumable definition
*
*  NOTES
*     MT-NOTE: map_consumable2str() is not safe 
*******************************************************************************/
const char * map_consumable2str(u_long32 op)
{
   static char *opv[] = {
      "NO",       /* CONSUMABLE_NO */
      "YES",      /* CONSUMABLE_YES */
      "JOB",      /* CONSUMABLE_JOB */
   };

   if (op > CONSUMABLE_JOB) {
      op = CONSUMABLE_NO;
   }
   return opv[op];
}

const char *
map_type2str(u_long32 type)
{
   static char *typev[] = {
      "??????",
      "INT",     /* TYPE_INT */
      "STRING",  /* TYPE_STR */
      "TIME",    /* TYPE_TIM */
      "MEMORY",  /* TYPE_MEM */
      "BOOL",    /* TYPE_BOO */
      "CSTRING", /* TYPE_CSTR */
      "HOST",    /* TYPE_HOST */
      "DOUBLE",  /* TYPE_DOUBLE */
      "RESTRING", /* TYPE_RESTR */

      "TYPE_ACC",/* TYPE_ACC */
      "TYPE_LOG",/* TYPE_LOG */
   };

   if (type < TYPE_FIRST || type > TYPE_LAST) {
      type = 0;
   }
   return typev[type];
}

/****** sgeobj/centry/centry_create() *****************************************
*  NAME
*     centry_create() -- Create a preinitialized centry element 
*
*  SYNOPSIS
*     lListElem *
*     centry_create(lList **answer_list, const char *name)
*
*  FUNCTION
*     Create a preinitialized centry element with the given "name".
*
*  INPUTS
*     lList **answer_list  - AN_Type 
*     const char *name     - full name 
*
*  RESULT
*     lListElem * - CE_Type element
*******************************************************************************/
lListElem *
centry_create(lList **answer_list, const char *name)
{
   lListElem *ret = NULL;  /* CE_Type */

   DENTER(CENTRY_LAYER, "centry_create");
   if (name != NULL) {
      ret = lCreateElem(CE_Type);
      if (ret != NULL) {
         lSetString(ret, CE_name, name);
         lSetString(ret, CE_shortcut, name);
         lSetUlong(ret, CE_valtype, TYPE_INT);
         lSetUlong(ret, CE_relop, CMPLXLE_OP);
         lSetUlong(ret, CE_requestable, REQU_NO);
         lSetUlong(ret, CE_consumable, CONSUMABLE_NO);
         lSetString(ret, CE_default, "1");
      } else {
         answer_list_add_sprintf(answer_list, STATUS_EMALLOC, 
                                 ANSWER_QUALITY_ERROR,
                                 MSG_MEM_MEMORYALLOCFAILED_S, SGE_FUNC);
      }
   } else {
      answer_list_add_sprintf(answer_list, STATUS_ERROR1, ANSWER_QUALITY_ERROR,
                              MSG_INAVLID_PARAMETER_IN_S, SGE_FUNC);
   }
   DRETURN(ret);
}

/****** sgeobj/centry/centry_is_referenced() **********************************
*  NAME
*     centry_is_referenced() -- Is centry element referenced?
*
*  SYNOPSIS
*     bool 
*     centry_is_referenced(const lListElem *centry, 
*                          lList **answer_list, 
*                          const lList *master_cqueue_list, 
*                          const lList *master_exechost_list, 
*                          const lList *master_sconf_list) 
*
*  FUNCTION
*     Is the centry element referenced in a sublist of
*     "master_queue_list", "master_exechost_list" or 
*     "master_sconf_list". 
*
*  INPUTS
*     const lListElem *centry           - CE_Type 
*     lList **answer_list               - AN_Type 
*     const lList *master_cqueue_list   - CQ_Type 
*     const lList *master_exechost_list - EH_Type 
*
*  RESULT
*     bool - true or false
*******************************************************************************/
bool 
centry_is_referenced(const lListElem *centry, lList **answer_list,
                     const lList *master_cqueue_list,
                     const lList *master_exechost_list,
                     const lList *master_rqs_list)
{
   bool ret = false;
   const char *centry_name = lGetString(centry, CE_name);

   DENTER(CENTRY_LAYER, "centry_is_referenced");

   if (sconf_is_centry_referenced(centry)) {
      answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN,
                              ANSWER_QUALITY_INFO, 
                              MSG_CENTRYREFINSCONF_S, centry_name);
      ret = true;
   }
   if (!ret) {
      lListElem *cqueue = NULL, *cel = NULL; 
 
      /* fix for bug 6422335
       * check the cq configuration for centry references instead of qinstances
       */
      for_each(cqueue, master_cqueue_list) {
         for_each(cel, lGetList(cqueue, CQ_consumable_config_list)) {
            if(lGetSubStr(cel, CE_name, centry_name, ACELIST_value)) {
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN,
                                       ANSWER_QUALITY_INFO, 
                                       MSG_CENTRYREFINQUEUE_SS,
                                       centry_name,
                                       lGetString(cqueue, CQ_name));
               ret = true;
               break;
            }
         }
         if (ret) {
            break;
         }
      }
   }
   if (!ret) {
      lListElem *host = NULL;    /* EH_Type */

      for_each(host, master_exechost_list) {
         if (host_is_centry_referenced(host, centry)) {
            const char *host_name = lGetHost(host, EH_name);

            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN,
                                    ANSWER_QUALITY_INFO, 
                                    MSG_CENTRYREFINHOST_SS,
                                    centry_name, host_name);
            ret = true;
            break;
         }
      }
   }
   if (!ret) {
      lListElem *rqs = NULL;
      for_each(rqs, master_rqs_list) {
         if (sge_centry_referenced_in_rqs(rqs, centry)) {
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN,
                                    ANSWER_QUALITY_INFO, 
                                    MSG_CENTRYREFINRQS_SS,
                                    centry_name, lGetString(rqs, RQS_name));
            ret = true;
            break;
         }
      }
   } 
      
   DRETURN(ret);
}

/****** sgeobj/centry/centry_print_resource_to_dstring() **********************
*  NAME
*     centry_print_resource_to_dstring() -- Print to dstring 
*
*  SYNOPSIS
*     bool 
*     centry_print_resource_to_dstring(const lListElem *this_elem, 
*                                      dstring *string) 
*
*  FUNCTION
*     Print resource string (memory, time) to dstring.
*
*  INPUTS
*     const lListElem *this_elem - CE_Type 
*     dstring *string            - dynamic string 
*
*  RESULT
*     bool - error state 
*        true  - success
*        false - error
*
*  NOTES
*     MT-NOTE: centry_print_resource_to_dstring() is MT safe
*******************************************************************************/
bool
centry_print_resource_to_dstring(const lListElem *this_elem, dstring *string)
{
   bool ret = true;

   DENTER(CENTRY_LAYER, "centry_print_resource_to_dstring");
   if (this_elem != NULL && string != NULL) {
      u_long32 type = lGetUlong(this_elem, CE_valtype);
      double val = lGetDouble(this_elem, CE_doubleval);

      switch (type) {
      case TYPE_TIM:
         double_print_time_to_dstring(val, string);
         break;
      case TYPE_MEM:
         double_print_memory_to_dstring(val, string);
         break;
      default:
         double_print_to_dstring(val, string);
         break;
      }
   }
   DRETURN(ret);
}

/****** sgeobj/centry/centry_list_get_master_list() ***************************
*  NAME
*     centry_list_get_master_list() -- return master list 
*
*  SYNOPSIS
*     lList ** centry_list_get_master_list(void) 
*
*  FUNCTION
*     Return master list. 
*
*  INPUTS
*     void - NONE 
*
*  RESULT
*     lList ** - CE_Type master list
*******************************************************************************/
lList **
centry_list_get_master_list(void)
{
   return object_type_get_master_list(SGE_TYPE_CENTRY);
}

/****** sgeobj/centry/centry_list_locate() ************************************
*  NAME
*     centry_list_locate() -- Find Centry element 
*
*  SYNOPSIS
*     lListElem *centry_list_locate(const lList *this_list, const char *name) 
*
*  FUNCTION
*     Find CEntry element with "name" in "this_list". 
*
*  INPUTS
*     const lList *this_list - CE_Type list 
*     const char *name       - name of an CE_Type entry 
*
*  RESULT
*     lListElem * - CE_Type element 
*******************************************************************************/
lListElem *
centry_list_locate(const lList *this_list, const char *name)
{
   lListElem *ret = NULL;   /* CE_Type */

   DENTER(CENTRY_LAYER, "centry_list_locate");
   if (this_list != NULL && name != NULL) {
      ret = lGetElemStr(this_list, CE_name, name);
      if (ret == NULL) {
         ret = lGetElemStr(this_list, CE_shortcut, name);
      }
   }
   DRETURN(ret);
}

/****** sgeobj/centry/centry_list_sort() **************************************
*  NAME
*     centry_list_sort() -- Sort a CE_Type list 
*
*  SYNOPSIS
*     bool centry_list_sort(lList *this_list) 
*
*  FUNCTION
*     Sort a CE_Type list 
*
*  INPUTS
*     lList *this_list - CE_Type list 
*
*  RESULT
*     bool - error state
*        true  - success
*        false - error
*******************************************************************************/
bool
centry_list_sort(lList *this_list)
{
   bool ret = true;

   DENTER(CENTRY_LAYER, "centry_list_sort");
   if (this_list != NULL) {
      lSortOrder *order = NULL;

      order = lParseSortOrderVarArg(lGetListDescr(this_list), "%I+", CE_name);
      lSortList(this_list, order);
      lFreeSortOrder(&order);
   }
   DRETURN(ret);
}

/****** sgeobj/centry/centry_list_init_double() *******************************
*  NAME
*     centry_list_init_double() -- Initialize double from string 
*
*  SYNOPSIS
*     bool centry_list_init_double(lList *this_list) 
*
*  FUNCTION
*     Initialize all double values contained in "this_list" 
*
*  INPUTS
*     lList *this_list - CE_Type list 
*
*  RESULT
*     bool - true
*******************************************************************************/
bool
centry_list_init_double(lList *this_list)
{
   bool ret = true;

   DENTER(CENTRY_LAYER, "centry_list_init_double");
   if (this_list != NULL) {
      lListElem *centry;

      for_each(centry, this_list) {
         double new_val = 0.0; /* 
                                * parse_ulong_val will not set it for all 
                                * data types! 
                                */
         parse_ulong_val(&new_val, NULL, lGetUlong(centry, CE_valtype),
                         lGetString(centry, CE_stringval), NULL, 0);
         lSetDouble(centry, CE_doubleval, new_val);
      }
   }
   DRETURN(ret);
}

/****** sgeobj/centry/centry_list_fill_request() ******************************
*  NAME
*     centry_list_fill_request() -- fills and checks list of complex entries
*
*  SYNOPSIS
*     int centry_list_fill_request(lList *centry_list,
*                                  lList *master_centry_list,
*                                  bool allow_non_requestable,
*                                  bool allow_empty_boolean,
*                                  bool allow_neg_consumable)
*
*  FUNCTION
*     This function fills a given list of complex entries with missing
*     attributes which can be found in the complex. It checks also
*     wether the given in the centry_list-List are valid.
*
*  INPUTS
*     lList *this_list           - resources as complex list CE_Type
*     lList **answer_list        - answer list
*     lList *master_centry_list  - the global complex list
*     bool allow_non_requestable - needed for qstat -l or qmon customize
*                                 dialog
*     int allow_empty_boolean    - boolean
*        true  - NULL values of boolean attributes will
*                be replaced with "true"
*        false - NULL values will be handled as error
*     int allow_neg_consumable  - boolean
*        true  - negative values for consumable
*                resources are allowed.
*        false - function will return with -1 if it finds
*                consumable resources with a negative value
*
*  RESULT
*     int - error
*        0 on success
*       -1 on error
*        an error message will be written into SGE_EVENT
*******************************************************************************/
int
centry_list_fill_request(lList *this_list, lList **answer_list, lList *master_centry_list,
                         bool allow_non_requestable, bool allow_empty_boolean,
                         bool allow_neg_consumable)
{
   lListElem *entry = NULL;
   lListElem *cep = NULL;

   DENTER(CENTRY_LAYER, "centry_list_fill_request");

   for_each(entry, this_list) {
      const char *name = lGetString(entry, CE_name);
      u_long32 requestable;

      cep = centry_list_locate(master_centry_list, name);
      if (cep != NULL) {
         requestable = lGetUlong(cep, CE_requestable);
         if (!allow_non_requestable && requestable == REQU_NO) {
/*             ERROR((SGE_EVENT, MSG_SGETEXT_RESOURCE_NOT_REQUESTABLE_S, name)); */
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_SGETEXT_RESOURCE_NOT_REQUESTABLE_S, name);
            DRETURN(-1);
         }

         /* replace name in request/threshold/consumable list,
            it may have been a shortcut */
         lSetString(entry, CE_name, lGetString(cep, CE_name));

         /* we found the right complex attrib */
         /* so we know the type of the requested data */
         lSetUlong(entry, CE_valtype, lGetUlong(cep, CE_valtype));

         /* we also know wether it is a consumable attribute */
         {
            /* With 6.2u2 the type for CE_consumable changed from bool to ulong
               for old objects we change the type to the new one */
            int pos = lGetPosViaElem(entry, CE_consumable, SGE_NO_ABORT);
            if (mt_get_type(entry->descr[pos].mt) == lBoolT) {
               DPRINTF(("Upgrading CE_consumable from bool to ulong\n"));
               entry->descr[pos].mt = cep->descr[pos].mt;
            }
         }
         lSetUlong(entry, CE_consumable, lGetUlong(cep, CE_consumable));

         if (centry_fill_and_check(entry, answer_list, allow_empty_boolean, allow_neg_consumable)) {
            /* no error msg here - centry_fill_and_check() makes it */
            DRETURN(-1);
         }
      } else {
         /* CLEANUP: message should be put into answer_list and
            returned via argument. */
/*          ERROR((SGE_EVENT, MSG_SGETEXT_UNKNOWN_RESOURCE_S, name)); */
         answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_SGETEXT_UNKNOWN_RESOURCE_S, name);
         DRETURN(-1);
      }
   }

   DRETURN(0);
}

bool
centry_list_are_queues_requestable(const lList *this_list) 
{
   bool ret = false;
   
   DENTER(CENTRY_LAYER, "centry_list_are_queues_requestable");
   if (this_list != NULL) {
      lListElem *centry = centry_list_locate(this_list, "qname");
      
      if (centry != NULL) {
         ret = (lGetUlong(centry, CE_requestable) != REQU_NO) ? true : false;
      }
   }
   DRETURN(ret);
}

const char *
centry_list_append_to_dstring(const lList *this_list, dstring *string)
{
   const char *ret = NULL;

   DENTER(CENTRY_LAYER, "centry_list_append_to_dstring");
   if (string != NULL) {
      lListElem *elem = NULL;
      bool printed = false;

      for_each(elem, this_list) {
         if (printed) {
            sge_dstring_append(string, ",");
         }
         sge_dstring_sprintf_append(string, "%s=", lGetString(elem, CE_name));
         if (lGetString(elem, CE_stringval) != NULL) {
            sge_dstring_append(string, lGetString(elem, CE_stringval));
         } else {
            sge_dstring_sprintf_append(string, "%f", 
                                       lGetDouble(elem, CE_doubleval));
         }
         printed = true;
      }
      if (!printed) {
         sge_dstring_append(string, "NONE");
      }
      ret = sge_dstring_get_string(string);
   }
   DRETURN(ret);
}

/* CLEANUP: should be replaced by centry_list_append_to_dstring() */
int
centry_list_append_to_string(lList *this_list, char *buff, 
                             u_long32 max_len)
{
   int attr_fields[] = { CE_name, CE_stringval, 0 };
   const char *attr_delis[] = {"=", ",", "\n"};
   int ret;

   DENTER(TOP_LAYER, "centry_list_append_to_string");

   if (buff)
      buff[0] = '\0';

   lPSortList(this_list, "%I+", CE_name);

   ret = uni_print_list(NULL, buff, max_len, this_list, attr_fields, attr_delis, 0);
   if (ret) {
      DRETURN(ret);
   }

   DRETURN(0);
}

/* CLEANUP: add answer_list remove SGE_EVENT */
/*
 * NOTE
 *    MT-NOTE: centry_list_parse_from_string() is MT safe
 */
lList *
centry_list_parse_from_string(lList *complex_attributes,
                              const char *str, bool check_value) 
{
   const char *cp;
   struct saved_vars_s *context = NULL;

   DENTER(TOP_LAYER, "centry_list_parse_from_string");

   /* allocate space for attribute list if no list is passed */
   if (complex_attributes == NULL) {
      if ((complex_attributes = lCreateList("qstat_l_requests", CE_Type)) == NULL) {
         ERROR((SGE_EVENT, MSG_PARSE_NOALLOCATTRLIST));
         DRETURN(NULL);
      }
   }

   /* str now points to the attr=value pairs */
   while ((cp = sge_strtok_r(str, ", ", &context))) {
      lListElem *complex_attribute = NULL;
      const char *attr = NULL;
      char *value = NULL;

      str = NULL;       /* for the next strtoks */

      /*
      ** recursive strtoks did not work
      */
      attr = cp;
      if ((value = strchr(cp, '='))) {
         *value++ = 0;
      }

      if (attr == NULL || *attr == '\0') {
         ERROR((SGE_EVENT, MSG_SGETEXT_UNKNOWN_RESOURCE_S, ""));
         lFreeList(&complex_attributes);
         sge_free_saved_vars(context);
         DRETURN(NULL);
      }

      if ((check_value) && (value == NULL || *value == '\0')) {
         ERROR((SGE_EVENT, MSG_CPLX_VALUEMISSING_S, attr));
         lFreeList(&complex_attributes);
         sge_free_saved_vars(context);
         DRETURN(NULL);
      }

   /* create new element, fill in the values and append it */
      if ( (complex_attribute= lGetElemStr(complex_attributes, CE_name, attr)) == NULL) {
         if ((complex_attribute = lCreateElem(CE_Type)) == NULL) {
            ERROR((SGE_EVENT, MSG_PARSE_NOALLOCATTRELEM));
            lFreeList(&complex_attributes);
            sge_free_saved_vars(context);
            DRETURN(NULL);
         }
         else {
            lSetString(complex_attribute, CE_name, attr);
            lAppendElem(complex_attributes, complex_attribute);
         }
      }
      
      lSetString(complex_attribute, CE_stringval, value);
   }

   sge_free_saved_vars(context);

   DRETURN(complex_attributes);
}

void
centry_list_remove_duplicates(lList *this_list) 
{
   DENTER(TOP_LAYER, "centry_list_remove_duplicates");
   cull_compress_definition_list(this_list, CE_name, CE_stringval, 0);
   DRETURN_VOID;
}


/****** sgeobj/centry/centry_elem_validate() **********************************
*  NAME
*     centry_elem_validate() -- validates a element and checks for duplicates 
*
*  SYNOPSIS
*     int centry_elem_validate(lListElem *centry,
*                                  lList *centry_list,
*                                  lList *answer_list)
*
*  FUNCTION
*     Checks weather the configuration within the new centry is okay or not. 
*     A centry is valid, when it satisfies the following rules:   
*         name 	  : has to be unique
*         Short cu  : has to be unique
*         Type	     : every type from the list (string, host, cstring, int, 
*                                               double, boolean, memory, time)
*         Consumable : can only be defined for: int, double, memory, time
*
*         Relational operator:
*         - for consumables:              only <=
*         - for non consumables:
*            - string, host, cstring:     only ==, !=
*            - boolean:	                  only ==
*            - int, double, memory, time: ==, !=, <=, <, =>, >
*
*         Requestable	   : for all attribute
*         default value 	: only for consumables
*
*     The type for build in attributes is not allowed to be changed!
*
*     When no centy list is passed in, the check for uniqie name and 
*     short cuts is skipt.
*
*  INPUTS
*     lListElem *centry     - the centry list, which should be validated
*     lList *centry_list    - if not null, the function checks, if the 
*                             centry element is already in the list
*     lList *answer_list    - contains the error messages
*
*  RESULT   
*     bool  false - error (the anwer_list contains the error message)
*           true - okay
*
*******************************************************************************/
bool centry_elem_validate(lListElem *centry, lList *centry_list, 
                          lList **answer_list) 
{
   u_long32 relop = lGetUlong(centry, CE_relop);
   u_long32 type = lGetUlong(centry, CE_valtype);
   const char *attrname = lGetString(centry, CE_name);
   const char *temp;
   bool ret = true;

   DENTER(TOP_LAYER, "centry_elem_validate");

   switch(type){
      case TYPE_INT :
      case TYPE_MEM :
      case TYPE_DOUBLE:
      case TYPE_TIM :
         if (relop == CMPLXEXCL_OP) {
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR,
                                    MSG_MUST_BOOL_TO_BE_EXCL_S, attrname);  
            ret = false;
         }
         break;
      
      case TYPE_STR :
      case TYPE_CSTR :
      case TYPE_RESTR:
      case TYPE_HOST : if ( !(relop == CMPLXEQ_OP || relop == CMPLXNE_OP) ) {
                           answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR,
                                                   MSG_INVALID_CENTRY_TYPE_RELOP_S, attrname);  
                           ret = false;
                       }
                       if (lGetUlong(centry, CE_consumable)) {
                           answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                                   MSG_INVALID_CENTRY_CONSUMABLE_TYPE_SS, attrname, 
                                                   map_type2str(type));
                           ret = false;
                       }
         break;

      case TYPE_BOO : if (relop != CMPLXEQ_OP && relop != CMPLXEXCL_OP){
                           answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR,
                                                   MSG_INVALID_CENTRY_TYPE_RELOP_S, attrname); 
                           ret = false;
                       } 
                       if (lGetUlong(centry, CE_consumable) && relop != CMPLXEXCL_OP) {
                           answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR,
                                                   MSG_INVALID_CENTRY_EXCL_S, attrname, 
                                                   map_type2str(type));
                           ret = false;
                       }
                       if (relop == CMPLXEXCL_OP && !lGetUlong(centry, CE_consumable)) {
                           answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR,
                                                   MSG_EXCL_MUST_BE_CONSUMABLE_S, attrname, 
                                                   map_type2str(type));
                           ret = false;
                       }

         break;

      default : /* error unknown type */
                  answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, 
                                    MSG_SGETEXT_UNKNOWN_ATTR_TYPE_U, sge_u32c(type));
                  ret = false;
         break;
   } 

   {
      double dval;
      char error_msg[200];
      error_msg[0] = '\0';

      /* donot allow REQUESTABLE for "tmpdir" attribute, refer CR6650497 */
      if (!strcmp(attrname, "tmpdir") && lGetUlong(centry, CE_requestable)!= REQU_NO) {
            answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR,
                                    MSG_CENTRY_NOTREQUESTABLE_S, attrname);
            ret = false;

      }

      if (lGetUlong(centry, CE_consumable)) {
  
         if (relop != CMPLXEXCL_OP && relop != CMPLXLE_OP) {
            answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR,
                                    MSG_INVALID_CENTRY_CONSUMABLE_RELOP_S , attrname);
            ret = false;
         }

         if (lGetUlong(centry, CE_requestable) == REQU_NO) {
            if(!parse_ulong_val(&dval, NULL, type, lGetString(centry, CE_default), error_msg, 199)){
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                       MSG_INVALID_CENTRY_PARSE_DEFAULT_SS, attrname, error_msg);
               ret = false;
            }
            if (dval == 0) {
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                       MSG_INVALID_CENTRY_CONSUMABLE_REQ1_S, attrname);
               ret = false;
            }
         } else if (lGetUlong(centry, CE_requestable) == REQU_FORCED) {
            if(!parse_ulong_val(&dval, NULL, type, lGetString(centry, CE_default), error_msg, 199)){
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                    MSG_INVALID_CENTRY_PARSE_DEFAULT_SS, attrname, error_msg);
               ret = false;
            }
            if (dval != 0) {
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                       MSG_INVALID_CENTRY_CONSUMABLE_REQ2_S, attrname);
               ret = false;
            }
         }
      } else if ( (temp = lGetString(centry, CE_default)) ) {
      
         switch(type){
            case TYPE_INT:
            case TYPE_TIM:
            case TYPE_MEM:
            case TYPE_BOO:
            case TYPE_DOUBLE:
      
               if(!parse_ulong_val(&dval, NULL, type, temp, error_msg, 199)){
                  answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                  MSG_INVALID_CENTRY_PARSE_DEFAULT_SS, attrname, error_msg);
                  ret = false;
               }

               /* accept non-zero default values for consumables only */
               if (dval != 0) {
                  answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                  MSG_INVALID_CENTRY_DEFAULT_S, attrname);
                  ret = false;
               }

               break;
            case TYPE_HOST:
            case TYPE_STR:
            case TYPE_RESTR:
            case TYPE_CSTR:
               if (strcasecmp(temp, "NONE") != 0 ) {
                  answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                  MSG_INVALID_CENTRY_DEFAULT_S, attrname);
                  ret = false;
               }
               break;
            default:
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, 
                                       MSG_SGETEXT_UNKNOWN_ATTR_TYPE_U, sge_u32c(type));
               ret = false;
         }
      }

      /* verify urgency always */
      if ((temp = lGetString(centry, CE_urgency_weight)) ) {
         switch(type){
            case TYPE_INT:
            case TYPE_TIM:
            case TYPE_MEM:
            case TYPE_BOO:
            case TYPE_DOUBLE:
            case TYPE_HOST:
            case TYPE_STR:
            case TYPE_CSTR:
            case TYPE_RESTR:
               if(!parse_ulong_val(&dval, NULL, TYPE_DOUBLE, temp, error_msg, 199)){
                  answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                       MSG_INVALID_CENTRY_PARSE_URGENCY_SS, attrname, error_msg);
                  ret = false;
               }
               break;

            default:
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, 
                                       MSG_SGETEXT_UNKNOWN_ATTR_TYPE_U, sge_u32c(type));
               ret = false;
         }
      }
   }


   /* check if its a build in value and if the type is correct */
   {
      int i; 
      int type = lGetUlong(centry, CE_valtype);
      for ( i=0; i< max_queue_resources; i++){
         if (strcmp(queue_resource[i].name, attrname) == 0 &&
            queue_resource[i].type != type){
            if ((queue_resource[i].type != TYPE_STR && queue_resource[i].type != TYPE_CSTR && queue_resource[i].type != TYPE_RESTR ) ||
                (type != TYPE_CSTR && type != TYPE_RESTR && type != TYPE_STR)){            

               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                       MSG_INVALID_CENTRY_TYPE_CHANGE_S, attrname);

               ret = false;
               break;
            }
         }
      }

      for ( i=0; i< max_host_resources; i++){
         if (strcmp(host_resource[i].name, attrname) == 0 &&
             host_resource[i].type != type){
            
            if ((host_resource[i].type != TYPE_STR && host_resource[i].type != TYPE_CSTR && host_resource[i].type != TYPE_RESTR ) ||
                (type != TYPE_CSTR && type != TYPE_RESTR && type != TYPE_STR)){
            
               answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , ANSWER_QUALITY_ERROR, 
                                       MSG_INVALID_CENTRY_TYPE_CHANGE_S, attrname);

               ret = false;
               break; 
            }
         }
      }
   }

   /* check for duplicates */
   if (centry_list) {
      const char *shortcut = lGetString(centry, CE_shortcut); 
      const lListElem *ce1 = centry_list_locate(centry_list, attrname);
      const lListElem *ce2 = centry_list_locate(centry_list, shortcut);

      /* 
       * if we already have a centry with this name or shortcut,
       * that is not the current centry -> cannot add/mod this one
       */
      if ((ce1 != NULL && ce1 != centry) ||
          (ce2 != NULL && ce2 != centry)) {
         answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN , 
                                 ANSWER_QUALITY_ERROR, 
                                 MSG_ANSWER_COMPLEXXALREADYEXISTS_SS, 
                                 attrname, shortcut);
         ret = false;
      }
   }
   DRETURN(ret);
}

/* EB: CLEANUP: change order of parameter */

/****** sgeobj/centry/centry_urgency_contribution() ***************************
*  NAME
*     centry_urgency_contribution() -- Compute urgency for a particular resource
*
*  SYNOPSIS
*     double 
*     centry_urgency_contribution(int slots, const char *name, double 
*                                 value, const lListElem *centry) 
*
*  FUNCTION
*     The urgency contribution for a particular resource 'name' is determined 
*     based on the 'slot' amount and using 'value' as per slot request. The
*     urgency value in the 'centry' element is used.
*
*  INPUTS
*     int slots               - The slot amount assumed.
*     const char *name        - The resource name.
*     double value            - The per slot request.
*     const lListElem *centry - The centry element (CE_Type)
*
*  RESULT
*     double - The resulting urgency contribution 
*
*  NOTES
*     MT-NOTES: centry_urgency_contribution() is MT safe
*******************************************************************************/
double 
centry_urgency_contribution(int slots, const char *name, double value, 
                            const lListElem *centry)
{
   double contribution, weight;
   const char *strval;
   u_long32 complex_type;
   
   DENTER(TOP_LAYER, "centry_urgency_contribution");

   if (!centry || 
       !(strval = lGetString(centry, CE_urgency_weight)) ||
       !(parse_ulong_val(&weight, NULL, TYPE_INT, strval, NULL, 0))) {
      DPRINTF(("no contribution for attribute\n"));
      DRETURN(0);
   }

   switch ((complex_type=lGetUlong(centry, CE_valtype))) {
   case TYPE_INT:
   case TYPE_TIM:
   case TYPE_MEM:
   case TYPE_BOO:
   case TYPE_DOUBLE:
      contribution = value * weight * slots;
      DPRINTF(("   %s: %7f * %7f * %d    ---> %7f\n", name, value, weight, slots, contribution));
      break;

   case TYPE_STR:
   case TYPE_CSTR:
   case TYPE_HOST:
   case TYPE_RESTR:
      contribution = weight;
      DPRINTF(("   %s: using weight as contrib ---> %7f\n", name, weight));
      break;

   default:
      ERROR((SGE_EVENT, MSG_SGETEXT_UNKNOWN_ATTR_TYPE_U, sge_u32c(complex_type)));
      contribution = 0;
      break;
   }

   DRETURN(contribution);
}

bool
centry_list_do_all_exists(const lList *this_list, lList **answer_list,
                          const lList *centry_list) 
{
   bool ret = true;
   lListElem *centry = NULL;
   
   DENTER(TOP_LAYER, "centry_list_do_all_exists");
   for_each(centry, centry_list) {
      const char *name = lGetString(centry, CE_name);

      if (centry_list_locate(this_list, name) == NULL) {
         answer_list_add_sprintf(answer_list, STATUS_EEXIST,
                                 ANSWER_QUALITY_ERROR,
                                 MSG_CQUEUE_UNKNOWNCENTRY_S, name);
         DTRACE;
         ret = false;
         break;
      }
   }
   DRETURN(ret);
}

bool
centry_list_is_correct(lList *this_list, lList **answer_list)
{

   bool ret = true;

   DENTER(TOP_LAYER, "centry_list_has_error");
   if (this_list != NULL) {
      lListElem *centry = lGetElemStr(this_list, CE_name, "qname");

      if (centry != NULL) {
         const char *value = lGetString(centry, CE_stringval);

         if (strchr(value, (int)'@')) {
            answer_list_add_sprintf(answer_list, STATUS_EEXIST,
                                    ANSWER_QUALITY_ERROR,
                                    MSG_CENTRY_QINOTALLOWED);
            ret = false;
         } 
      }
   }
   
/* do complex attributes syntax verification */
  if (ret) {  
    lListElem *elem;
    for_each(elem, this_list){
      ret = object_verify_expression_syntax(elem, answer_list);
      if(!ret) break;
    }
  }   
   DRETURN(ret);
}

int ensure_attrib_available(lList **alpp, lListElem *ep, int nm) 
{
   int ret = 0;
   lListElem *attr = NULL;

   DENTER(TOP_LAYER, "ensure_attrib_available");
   if (ep != NULL) {
      for_each (attr, lGetList(ep, nm)) {
         const char *name = lGetString(attr, CE_name);
         lListElem *centry = centry_list_locate(*object_type_get_master_list(SGE_TYPE_CENTRY), name);

         if (centry == NULL) {
            ERROR((SGE_EVENT, MSG_GDI_NO_ATTRIBUTE_S, 
                   name != NULL ? name : "<noname>"));
            answer_list_add(alpp, SGE_EVENT, 
                            STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR);
            ret = STATUS_EUNKNOWN;
            break;
         } else {
            const char *fullname = lGetString(centry, CE_name);

            /*
             * Replace shortcuts by the fullname silently 
             */
            if (strcmp(fullname, name) != 0) {
               lSetString(attr, CE_name, fullname);
            }
         }
      }
   }
   DRETURN(ret);
}

/****** sge_centry/validate_load_formula() ********************
*  NAME
*     validate_load_formula() 
*
*  SYNOPSIS
*     bool validate_load_formula(lListElem *schedd_conf, lList 
*     **answer_list, lList *centry_list, const char *name) 
*
*  FUNCTION
*     The function validates a load formula string.
*
*  INPUTS
*     const char *load_formula - string that should be a valid load formula
*     lList **answer_list      - error messages
*     lList *centry_list       - list of defined complex values 
*     const char*              - name (used for error messages)
*
*  RESULT
*     bool - true if valid
*            false if unvalid 
*
* MT-NOTE: is MT-safe, works only on the passed in data
*
*******************************************************************************/
bool validate_load_formula(const char *load_formula, lList **answer_list, lList *centry_list, const char *name) {
   bool ret = true;

   DENTER(TOP_LAYER, "validate_load_formual");

   /* Check for keyword 'none' */
   if (!strcasecmp(load_formula, "none")) {
      SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_NONE_NOT_ALLOWED_S, name));
      answer_list_add(answer_list, SGE_EVENT, STATUS_ESYNTAX, 
                      ANSWER_QUALITY_ERROR);
      ret = false;
   }

   /* Check complex attributes and type */
   if (ret == true) {
      const char *term_delim = "+-";
      const char *term, *next_term;
      struct saved_vars_s *term_context = NULL;

      next_term = sge_strtok_r(load_formula, term_delim, &term_context);
      while ((term = next_term) && ret == true) {
         const char *fact_delim = "*";
         const char *fact, *next_fact, *end;
         lListElem *cmplx_attr = NULL;
         struct saved_vars_s *fact_context = NULL;
         
         next_term = sge_strtok_r(NULL, term_delim, &term_context);

         fact = sge_strtok_r(term, fact_delim, &fact_context);
         next_fact = sge_strtok_r(NULL, fact_delim, &fact_context);
         end = sge_strtok_r(NULL, fact_delim, &fact_context);

         /* first factor has to be a complex attr */
         if (fact != NULL) {
            if (strchr(fact, '$')) {
               fact++;
            }
            cmplx_attr = centry_list_locate(centry_list, fact);

            if (cmplx_attr != NULL) {
               int type = lGetUlong(cmplx_attr, CE_valtype);

               if (type == TYPE_STR || type == TYPE_CSTR || type == TYPE_HOST || type == TYPE_RESTR) {
                  SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_WRONGTYPE_ATTRIBUTE_SS, name,
                                         fact));
                  answer_list_add(answer_list, SGE_EVENT, 
                                  STATUS_ESYNTAX, ANSWER_QUALITY_ERROR);
                  ret = false;
               }
            } else if (!sge_str_is_number(fact)) {
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_NOTEXISTING_ATTRIBUTE_SS, 
                              name, fact));
               answer_list_add(answer_list, SGE_EVENT, 
                               STATUS_ESYNTAX, ANSWER_QUALITY_ERROR);
               ret = false;
            }
         }
         /* is weighting factor a number? */
         if (next_fact != NULL) {
            if (!sge_str_is_number(next_fact)) {
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_WEIGHTFACTNONUMB_SS, name,
                              next_fact));
               answer_list_add(answer_list, SGE_EVENT, 
                               STATUS_ESYNTAX, ANSWER_QUALITY_ERROR);
               ret = false;
            }
         }

         /* multiple weighting factors? */
         if (end != NULL) {
            SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_MULTIPLEWEIGHTFACT_S, name));
            answer_list_add(answer_list, SGE_EVENT, 
                            STATUS_ESYNTAX, ANSWER_QUALITY_ERROR);
            ret = false;
         }
         sge_free_saved_vars(fact_context);
      }
      sge_free_saved_vars(term_context);
   }

   DRETURN(ret);
}

/****** sge_centry/load_formula_is_centry_referenced() *************************
*  NAME
*     load_formula_is_centry_referenced() -- search load formula for centry reference
*
*  SYNOPSIS
*     bool load_formula_is_centry_referenced(const char *load_formula, const 
*     lListElem *centry) 
*
*  FUNCTION
*     This function searches for a centry reference in the defined algebraic
*     expression
*
*  INPUTS
*     const char *load_formula - load formula expression
*     const lListElem *centry  - centry to search for
*
*  RESULT
*     bool - true if referenced
*            false if not referenced
*
*  NOTES
*     MT-NOTE: load_formula_is_centry_referenced() is MT safe 
*
*******************************************************************************/
bool load_formula_is_centry_referenced(const char *load_formula, const lListElem *centry)
{
   bool ret = false;
   const char *term_delim = "+-";
   const char *term, *next_term;
   struct saved_vars_s *term_context = NULL;
   const char *centry_name = lGetString(centry, CE_name);

   DENTER(TOP_LAYER, "load_formula_is_centry_referenced");

   if (load_formula == NULL) {
      DRETURN(ret);
   }

   next_term = sge_strtok_r(load_formula, term_delim, &term_context);
   while ((term = next_term) && ret == false) {
      const char *fact_delim = "*";
      const char *fact;
      struct saved_vars_s *fact_context = NULL;
      
      next_term = sge_strtok_r(NULL, term_delim, &term_context);

      fact = sge_strtok_r(term, fact_delim, &fact_context);
      if (fact != NULL) {
         if (strchr(fact, '$')) {
            fact++;
         }
         if (strcmp(fact, centry_name) == 0) {
            ret = true;
         }
      }
      sge_free_saved_vars(fact_context);
   }
   sge_free_saved_vars(term_context);

   DRETURN(ret);
}
