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

#ifdef SOLARISAMD64
#  include <sys/stream.h>
#endif   

#include "basis_types.h"
#include "sgermon.h" 
#include "sge_string.h"
#include "sge_str.h"
#include "sge_log.h"
#include "sge_answer.h"
#include "sge_hostname.h"
#include "sge_attr.h"
#include "sge_href.h"
#include "sge_hgroup.h"
#include "sge_object.h"
#include "sge_stdlib.h"
#include "commlib.h"

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

#define HOSTATTR_LAYER BASIS_LAYER

#define TEMPLATE_ATTR_IMPL(PREFIX, TYPE, INTERNAL_TYPE,                       \
                           DESCRIPTOR, HREF_NM, VALUE_NM)                     \
                                                                              \
lListElem *                                                                   \
PREFIX##_create(lList **answer_list, const char *href, TYPE value)            \
{                                                                             \
   return attr_create(answer_list, href, &value,                              \
                      DESCRIPTOR, HREF_NM, VALUE_NM);                         \
}                                                                             \
                                                                              \
lListElem *                                                                   \
PREFIX##_list_find(const lList *this_list, const char *href)                  \
{                                                                             \
   return lGetElemHost(this_list, HREF_NM, href);                             \
}                                                                             \
                                                                              \
bool                                                                          \
PREFIX##_list_add(lList **this_list, lList **answer_list, lListElem **attr,   \
                  int flags, lList **ambiguous_href_list)                     \
{                                                                             \
   return attr_list_add(this_list, answer_list, attr, flags,                  \
                        ambiguous_href_list,                                  \
                        DESCRIPTOR, HREF_NM, VALUE_NM);                       \
}                                                                             \
                                                                              \
bool                                                                          \
PREFIX##_list_add_set_del(lList **this_list, lList **answer_list,             \
                         const char *hostname, void *value, bool remove)      \
{                                                                             \
   return attr_list_add_set_del(this_list, answer_list, hostname,             \
                        value, remove, DESCRIPTOR, HREF_NM, VALUE_NM);        \
}                                                                             \
                                                                              \
bool                                                                          \
PREFIX##_list_find_value(const lList *this_list, lList **answer_list,         \
                         const char *hostname, INTERNAL_TYPE *value,          \
                         const char **mastching_host_or_group,                \
                         const char **matching_group,                         \
                         bool *is_ambiguous)                                  \
{                                                                             \
   return attr_list_find_value(this_list, answer_list, hostname,              \
                               value, mastching_host_or_group,                \
                               matching_group, is_ambiguous,                  \
                               DESCRIPTOR, HREF_NM, VALUE_NM);                \
}                                                                             \
                                                                              \
bool                                                                          \
PREFIX##_list_find_value_href(const lList *this_list, lList **answer_list,    \
                         const char *hostname, INTERNAL_TYPE *value,          \
                         bool *found)                                         \
{                                                                             \
   return attr_list_find_value_href(this_list, answer_list, hostname,         \
                               value, found, DESCRIPTOR, HREF_NM,             \
                               VALUE_NM);                                     \
}                                                                             \
                                                                              \
bool                                                                          \
PREFIX##_list_append_to_dstring(const lList *this_list, dstring *string)      \
{                                                                             \
   return attr_list_append_to_dstring(this_list, string,                      \
                                      DESCRIPTOR, HREF_NM, VALUE_NM);         \
}                                                                             \
                                                                              \
bool                                                                          \
PREFIX##_list_parse_from_string(lList **this_list, lList **answer_list,       \
                                const char *string, int flags)                \
{                                                                             \
   return attr_list_parse_from_string(this_list, answer_list, string,         \
                                      flags, DESCRIPTOR, HREF_NM, VALUE_NM);  \
}                                                                             \
                                                                              \
lListElem *                                                                   \
PREFIX##_list_locate(const lList *this_list, const char *host_or_group)       \
{                                                                             \
   return attr_list_locate(this_list, host_or_group, HREF_NM);                \
}                                                                             
 
static lListElem *
attr_create(lList **answer_list, const char *href, void *value,
            const lDescr *descriptor, int href_nm, int value_nm);

static bool 
attr_list_add(lList **this_list, lList **answer_list, lListElem **attr, 
              int flags, lList **ambiguous_href_list,
              const lDescr *descriptor, int href_nm, int value_nm);

static bool 
attr_list_add_set_del(lList **this_list, lList **answer_list, 
              const char *hostname, void *value_buffer, bool remove,
              const lDescr *descriptor, int href_nm, int value_nm);

static bool
attr_list_find_value(const lList *this_list, lList **answer_list, 
                     const char *hostname, void *value_buffer, 
                     const char **matching_host_or_group,
                     const char **matching_group,
                     bool *is_ambiguous, const lDescr *descriptor, 
                     int href_nm, int value_nm);

static bool
attr_list_find_value_href(const lList *this_list, lList **answer_list, 
                     const char *hostname, void *value_buffer, 
                     bool *found, const lDescr *descriptor, 
                     int href_nm, int value_nm);

static bool
attr_list_parse_from_string(lList **this_list, lList **answer_list,
                            const char *string, int flags,
                            const lDescr *descriptor, int href_nm, 
                            int value_nm);

static lListElem *
attr_list_locate(const lList *this_list, const char *host_or_group, 
                 int href_nm);


/* EB: ADOC: add commets */

/*
descriptor        ASTR_Type
href_nm           ASTR_href
value_nm          ASTR_value
*/
static lListElem *
attr_create(lList **answer_list, const char *href, void *value,
            const lDescr *descriptor, int href_nm, int value_nm)
{
   lListElem *ret = NULL;

   DENTER(HOSTATTR_LAYER, "attr_create");
   if (href != NULL) {
      lListElem *new_attr = lCreateElem(descriptor);

      if (new_attr != NULL) {
         lSetHost(new_attr, href_nm, href);
         if (value != NULL) {
            object_set_any_type(new_attr, value_nm, value);
         }
         ret = new_attr;
      } else {
         SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_NONEWATTRSETTING_S, href));
         answer_list_add(answer_list, SGE_EVENT, 
                         STATUS_ERROR1, ANSWER_QUALITY_ERROR);
      }
   } else {
      SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_INAVLID_PARAMETER_IN_S, SGE_FUNC));
      answer_list_add(answer_list, SGE_EVENT, 
                      STATUS_ERROR1, ANSWER_QUALITY_ERROR);
   }
   DEXIT;
   return ret;
}

/*
descriptor        ASTR_Type
href_nm           ASTR_href
value_nm          ASTR_value
*/
static bool 
attr_list_add(lList **this_list, lList **answer_list, lListElem **attr, 
              int flags, lList **ambiguous_href_list,
              const lDescr *descriptor, int href_nm, int value_nm)
{
   bool ret = false;

   DENTER(HOSTATTR_LAYER, "attr_list_add");
   if (this_list != NULL && attr != NULL && *attr != NULL) {
      lListElem *attr_elem = NULL; 
      const char *href = NULL;
      bool is_hgroup = false; 
      bool created_list = false;

      href = lGetHost(*attr, href_nm);
      is_hgroup = sge_is_hgroup_ref(href);
      attr_elem = attr_list_locate(*this_list, href, href_nm);

      if (*this_list == NULL) {
         *this_list = lCreateList("", descriptor);
         created_list = true;
      }

      /*
       * HOSTREF_DEFAULT and host reference values can be added/changed
       * Hostgroup entries already contained in the list might be changed.
`      *
       * New hostgroup references might cause a conflict. We have to
       * make additional checks.
       */
      if (!strcmp(href, HOSTREF_DEFAULT) || 
          !is_hgroup ||
          (is_hgroup && attr_elem != NULL)) {
         const char *value = NULL; 

         object_get_any_type(*attr, value_nm, &value);
         if (attr_elem != NULL) {
            if (flags & HOSTATTR_OVERWRITE) {
               object_set_any_type(attr_elem, value_nm, &value);
               lFreeElem(*attr);
/* FIXME ??? */               
               *attr = attr_elem;
               ret = true;
            } else {
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, 
                              MSG_ATTR_VALUEMULDEFINED_S, href));
               answer_list_add(answer_list, SGE_EVENT,
                               STATUS_ERROR1, ANSWER_QUALITY_ERROR);
            }
         } else {
            lAppendElem(*this_list, *attr);
/* FIXME ????            *attr = NULL; */
            ret = true;
         }
      } else {
         lList *href_list = NULL;
         lList *host_list = NULL;
         lList *new_host_list = NULL;
         bool lret = true;
       
         /*
          * Create list of ambiguous hosts only 
          *  - if ambiguious entries are not allowed or
          *  - if the the caller of this functions expects this
          *    function to return it
          */ 
         if (!(flags & HOSTATTR_ALLOW_AMBIGUITY) && 
             ambiguous_href_list != NULL) {

            /*
             * Create host reference list of all used hostgroups
             * (except HOSTREF_DEFAULT, and host entries)
             */
            if (lret) {
               for_each(attr_elem, *this_list) {
                  const char *href = lGetHost(attr_elem, ASTR_href); 

                  if (strcmp(href, HOSTREF_DEFAULT) && 
                      sge_is_hgroup_ref(href)) {
                     lret &= href_list_add(&href_list, NULL, href);
                  }
               }
            }

            /*
             * Find all directly or indirectly referenced hosts for all 
             * hostgroups
             */
            if (lret && href_list != NULL) {
               lList *master_list = *(hgroup_list_get_master_list());
               lList *tmp_href_list = NULL; 

               lret &= href_list_find_all_references(href_list, NULL, 
                                                     master_list, &host_list, 
                                                     NULL); 
               lret &= href_list_add(&tmp_href_list, NULL, href);
               lret &= href_list_find_all_references(tmp_href_list, NULL,
                                                     master_list, 
                                                     &new_host_list, NULL);
               lFreeList(tmp_href_list);
            }

            /*
             * Find all host references which already have a value attached.
             * For all these hosts the new value in ambiguious.
             */
            if (lret && ambiguous_href_list != NULL && host_list != NULL &&
                new_host_list != NULL) { 
               lret = href_list_compare(new_host_list, NULL, host_list,
                                           NULL, NULL, ambiguous_href_list,
                                           NULL);
            }
         } 

         if (ambiguous_href_list != NULL &&
               lGetNumberOfElem(*ambiguous_href_list) >= 1 &&
               !(flags & HOSTATTR_ALLOW_AMBIGUITY)) {
            SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_RESULTUNAMBIGUOUS_S));
            answer_list_add(answer_list, SGE_EVENT,
                            STATUS_ERROR1, ANSWER_QUALITY_ERROR);
         } else {
            lAppendElem(*this_list, *attr);
            ret = true;
         }
      }

      if (created_list == true && ret == false) {
         lFreeList(*this_list);
      }
   } 
   DEXIT;
   return ret;
}

static bool 
attr_list_add_set_del(lList **this_list, lList **answer_list, 
              const char *hostname, void *value, bool remove,
              const lDescr *descriptor, 
              int href_nm, int value_nm)
{
   bool ret = True;
   lListElem *attr = NULL;

   if (remove && this_list && *this_list) {
      attr = attr_list_locate(*this_list, hostname, href_nm);
      lRemoveElem(*this_list, attr);
      return ret;
   }   
      
   attr = attr_create(answer_list, hostname, value, descriptor, 
                         href_nm, value_nm);
   ret = attr_list_add(this_list, answer_list,
                        &attr, HOSTATTR_OVERWRITE, NULL,
                        descriptor, href_nm, value_nm);
/* FIXME ???   lFreeElem(attr); */

   return ret;
}


/*
descriptor        ASTR_Type
href_nm           ASTR_href
value_nm          ASTR_value
*/
static bool
attr_list_find_value(const lList *this_list, lList **answer_list, 
                     const char *hostname, void *value_buffer,
                     const char **matching_host_or_group,
                     const char **matching_group,
                     bool *is_ambiguous, const lDescr *descriptor, 
                     int href_nm, int value_nm)
{
   bool ret = false;

   DENTER(HOSTATTR_LAYER, "attr_list_find_value");

   if (this_list != NULL && hostname != NULL) {
      lListElem *href = NULL;
   
      /*
       * Try to find a value for the concerned host
       */ 
      href = attr_list_locate(this_list, hostname, href_nm);
      if (href != NULL) {  
         object_get_any_type(href, value_nm, value_buffer);
         DPRINTF(("Found value for host "SFQ"\n", hostname));
         ret = true;
      } else {
         bool already_found = false;

         *is_ambiguous = false;
         /*
          * Try to find a value for all hgroup definitions
          * if there was no host related value
          *
          * Exit the loop as soon as possible! This will be the case if
          * an ambiguous value for the concerned host will be found.
          */
         for_each(href, this_list) {
            const char *href_name = lGetHost(href, href_nm);
            lList *master_list = *(hgroup_list_get_master_list());
            bool lret = true;

            if (strcmp(href_name, HOSTREF_DEFAULT) && 
                sge_is_hgroup_ref(href_name)) {
               lList *tmp_href_list = NULL;
               lListElem *tmp_href = NULL;
               lList *host_list = NULL;

               href_list_add(&tmp_href_list, NULL, href_name);
               lret &= href_list_find_all_references(tmp_href_list, NULL,
                                                     master_list, &host_list,
                                                     NULL); 
               tmp_href = href_list_locate(host_list, hostname);
               if (tmp_href != NULL) {
                  if (already_found == false) {
                     already_found = true;
                     object_get_any_type(href, value_nm, value_buffer);
                     *matching_host_or_group = href_name;
                     DPRINTF(("Found value for domain "SFQ"\n", href_name));
                     ret = true;
                  } else {
                     *is_ambiguous = true;
                     *matching_group = href_name;
                     DPRINTF(("Found ambiguous value in domain "SFQ"\n", 
                               href_name));
                     ret = false;
                     break;
                  }
               }
               host_list = lFreeList(host_list);
               tmp_href_list = lFreeList(tmp_href_list);
            }
         }
         if (ret == false) {
            lListElem *tmp_href = NULL;

            /*
             * Use the default value
             */
            tmp_href = attr_list_locate(this_list, HOSTREF_DEFAULT, href_nm);
            if (tmp_href != NULL) {
               DPRINTF(("Using default value\n"));
               object_get_any_type(tmp_href, value_nm, value_buffer);
               ret = true;
            } else {
               /*
                * Should never happen.
                */
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_NOCONFVALUE));
               answer_list_add(answer_list, SGE_EVENT,
                               STATUS_ERROR1, ANSWER_QUALITY_ERROR);            
            }
         }
      }
      if (ret) {
         DTRACE;
      }
   }
   DEXIT;
   return ret;
}

/*
descriptor        ASTR_Type
href_nm           ASTR_href
value_nm          ASTR_value
*/
static bool
attr_list_find_value_href(const lList *this_list, lList **answer_list, 
                     const char *hostname, void *value_buffer,
                     bool *found, const lDescr *descriptor, 
                     int href_nm, int value_nm)
{
   bool ret = false;

   DENTER(HOSTATTR_LAYER, "attr_list_find_value");

   if (this_list != NULL && hostname != NULL) {
      lListElem *href = NULL;
   
      /*
       * Try to find a value for the concerned host
       */ 
      href = attr_list_locate(this_list, hostname, href_nm);
      if (href != NULL) {  
         object_get_any_type(href, value_nm, value_buffer);
         *found = True;
         DTRACE;
         ret = true;
      } else {
         if (ret == false) {
            lListElem *tmp_href = NULL;

            /*
             * Use the default value
             */
            tmp_href = attr_list_locate(this_list, HOSTREF_DEFAULT, href_nm);
            if (tmp_href != NULL) {
               object_get_any_type(tmp_href, value_nm, value_buffer);
               *found = False;
               DTRACE;
               ret = true;
            } else {
               /*
                * Should never happen.
                */
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_NOCONFVALUE));
               answer_list_add(answer_list, SGE_EVENT,
                               STATUS_ERROR1, ANSWER_QUALITY_ERROR);            
            }
         }
      }
      if (ret) {
         DTRACE;
      }
   }
   DEXIT;
   return ret;
}

/*
descriptor        ASTR_Type
href_nm           ASTR_href
value_nm          ASTR_value
*/
bool
attr_list_append_to_dstring(const lList *this_list, dstring *string,
                            const lDescr *descriptor, int href_nm, int value_nm)
{
   bool ret = true;
   bool found_default = false;
   bool found_group = false;
   bool found_host = false;
   lListElem *attr = NULL;
   dstring default_string = DSTRING_INIT;
   dstring group_string = DSTRING_INIT;
   dstring host_string = DSTRING_INIT;
   const char *const comma = ",";

   DENTER(HOSTATTR_LAYER, "attr_list_append_to_dstring");

   for_each(attr, this_list) {
      const char *href;

      href = lGetHost(attr, href_nm);

      if (href != NULL && !strcmp(href, HOSTREF_DEFAULT)) {
         object_append_field_to_dstring(attr, NULL, &default_string, value_nm,
                                        '\0');
         found_default = true;
      } else if (sge_is_hgroup_ref(href)) {
         if (found_group) {
            sge_dstring_append(&group_string, comma);
            
         }
         sge_dstring_sprintf_append(&group_string, "[%s=", href);
         object_append_field_to_dstring(attr, NULL, &group_string, value_nm, 
                                        '\0');
         sge_dstring_sprintf_append(&group_string, "]");
         found_group = true;
      } else {
         if (found_host) {
            sge_dstring_append(&host_string, comma);
         }
         sge_dstring_sprintf_append(&host_string, "[%s=", href);
         object_append_field_to_dstring(attr, NULL, &host_string, value_nm,
                                        '\0');
         sge_dstring_append(&host_string, "]");
         found_host = true;
      }
   }
   if (found_default) {
      const char *s = sge_dstring_get_string(&default_string);

      sge_dstring_append(string, s);
   }
   if (found_group) {
      const char *s = sge_dstring_get_string(&group_string);
      if (found_default) {
         sge_dstring_append(string, comma);
      }
      sge_dstring_append(string, s);
   }
   if (found_host) {
      const char *s = sge_dstring_get_string(&host_string);
      if (found_group || found_default) {
         sge_dstring_append(string, comma);
      }
      sge_dstring_append(string, s);
   }
   if (!found_default && !found_group && !found_host) {
      sge_dstring_append(string, "NONE");
   }
   sge_dstring_free(&default_string);
   sge_dstring_free(&group_string);
   sge_dstring_free(&host_string);
   DEXIT;
   return ret;
}

/*
descriptor        ASTR_Type
href_nm           ASTR_href
value_nm          ASTR_value
*/
static bool
attr_list_parse_from_string(lList **this_list, lList **answer_list,
                            const char *string, int flags,
                            const lDescr *descriptor, int href_nm, 
                            int value_nm)
{
   bool ret = true;
   DENTER(TOP_LAYER, "attr_list_parse_from_string");
  
   if (this_list != NULL && string != NULL) { 
      struct saved_vars_s *strtok_context = NULL;
      char *token = NULL;
      char *next_token = NULL;
      bool is_first_token = true;
      bool is_last_token = false;

      /* 
       * start         := value {',' group_value} .
       * group_value   := '[' group_or_host '=' value ']' . 
       * group_or_host := ['@'] name .
       * value         := <depends on listtype>
       * 
       * example: lic=5,fast=1,[@group=lic=4,fast=0],[b=lic=0] 
       *
       * lic=5,fast=1, 
       * @group=lic=4,fast=0], 
       * b=lic=0]
       * 
       * lic=5,fast=1
       * @group=lic=4,fast=0
       * b=lic=0
       *
       * default  lic=5,fast=1
       * @group   lic=4,fast=0
       * b        lic=0
       * 
       */

      next_token = sge_strtok_r(string, "[", &strtok_context);
      while (ret && (token = next_token)) {
         size_t length; 

         next_token = sge_strtok_r(NULL, "[", &strtok_context);
         if (next_token == NULL) {
            is_last_token = true;
         }

         /*
          * There might be white space at the end of each token.
          */
         sge_strip_white_space_at_eol(token);
         length = strlen(token);

         if (length >= 1) {
            const char *href_name = NULL;
            char *value = NULL;
            bool first_is_default = true;
  
            /* 
             * All except the last token has to conatin a ',' as last
             * character in the string. This ',' has to be removed.
             */
            if (ret && !is_last_token) {
               if (token[length - 1] == ',') {
                  token[length - 1] = '\0';
                  length--;
               } else {
                  SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_MISSINGCOMMA_S,
                                         string));
                  answer_list_add(answer_list, SGE_EVENT,
                                  STATUS_ERROR1, ANSWER_QUALITY_ERROR);
                  ret = false;
               }
            }
            else if (ret && is_last_token && (token[length - 1] == ',')) {
               SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_TRAILINGCOMMA_S,
                                      string));
               answer_list_add(answer_list, SGE_EVENT,
                               STATUS_ERROR1, ANSWER_QUALITY_ERROR);
               ret = false;
            }

            /*
             * There might be space after a closing brace ']'
             */
            sge_strip_white_space_at_eol(token);
            length = strlen(token);

            /* 
             * All except the first token has to end with a ']'. Also
             * this charcter has to be removed.
             */
            if (ret && !is_first_token) {
               if (token[length - 1] == ']') {
                  token[length - 1] = '\0';
                  length--;
               } else {
                  SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_MISSINGBRACKET_S,
                                         string));
                  answer_list_add(answer_list, SGE_EVENT,
                                  STATUS_ERROR1, ANSWER_QUALITY_ERROR);
                  ret = false;
               }
            }

            /*
             * If the first token containes a ']' as last charcter than
             * a default value is missing! This is not a error but
             * we have to parse a group or host additionally
             */ 
            if (ret && is_first_token) {
               if (token[length - 1] == ']') {
                  token[length - 1] = '\0';
                  length--;
                  first_is_default = false;
               } 
            }

            /*
             * All but the first token has to contain a hostgroup
             * or a host reference in the beginning of the string 
             */
            if (ret) {
               if (!is_first_token || !first_is_default) {
                  value = strchr(token, '=');
                  href_name = token;

                  if (value != NULL) {
                     value[0] = '\0';
                     value++;
                  } else {
                     SGE_ADD_MSG_ID(sprintf(SGE_EVENT, 
                                            MSG_ATTR_EQUALSIGNEXPRECTED));
                     answer_list_add(answer_list, SGE_EVENT,
                                     STATUS_ERROR1, ANSWER_QUALITY_ERROR);
                     ret = false;
                  }
               } else {
                  href_name = HOSTREF_DEFAULT;
                  value = token;
               }
            }

            /*
             * Parsing the token was successfull. We can create a new 
             * element.
             */
            if (ret) {
               lListElem *attr_elem = NULL;
     
               attr_elem = attr_create(answer_list, href_name, NULL,
                                       descriptor, href_nm, value_nm);
               if (attr_elem != NULL) {
                  ret &= object_parse_field_from_string(attr_elem, 
                                                        answer_list,
                                                        value_nm, value);
                  if (ret) {
                     ret &= attr_list_add(this_list, answer_list,
                                          &attr_elem, flags, NULL,
                                          descriptor, href_nm, value_nm);
                  } else {
                     SGE_ADD_MSG_ID(sprintf(SGE_EVENT, 
                                    MSG_ATTR_PARSINGERROR_S, value));
                     answer_list_add(answer_list, SGE_EVENT,
                                     STATUS_ERROR1, ANSWER_QUALITY_ERROR);
                  }
                  if (!ret) {
                     attr_elem = lFreeElem(attr_elem);
                  }
               } else {
                  ret = false;
               }
            }
         } else {
            SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_ATTR_NOVALUEGIVEN));
            answer_list_add(answer_list, SGE_EVENT,
                            STATUS_ERROR1, ANSWER_QUALITY_ERROR);
            ret = false;
         }
         is_first_token = false;
      } 
      sge_free_saved_vars(strtok_context);
      strtok_context = NULL;
   } else {
      SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_INAVLID_PARAMETER_IN_S, SGE_FUNC));
      answer_list_add(answer_list, SGE_EVENT, 
                      STATUS_ERROR1, ANSWER_QUALITY_ERROR);
      ret = false;
   } 

   DEXIT;
   return ret;
}

/*
href_nm           ASTR_href
*/
static lListElem *
attr_list_locate(const lList *this_list, const char *host_or_group, int href_nm)
{
   lListElem *ret = NULL;

   DENTER(HOSTATTR_LAYER, "attr_list_locate");
   if (this_list != NULL && host_or_group != NULL) {
      ret = lGetElemHost(this_list, href_nm, host_or_group);
   }
   DEXIT;
   return ret; 
}

TEMPLATE_ATTR_IMPL(str_attr, const char *, const char *, ASTR_Type, ASTR_href, ASTR_value) 

TEMPLATE_ATTR_IMPL(ulng_attr, u_long32, u_long32, AULNG_Type, AULNG_href, AULNG_value) 

TEMPLATE_ATTR_IMPL(bool_attr, bool, bool, ABOOL_Type, ABOOL_href, ABOOL_value) 

TEMPLATE_ATTR_IMPL(time_attr, const char *, const char *, ATIME_Type, ATIME_href, ATIME_value) 

TEMPLATE_ATTR_IMPL(mem_attr, const char *, const char *, AMEM_Type, AMEM_href, AMEM_value) 

TEMPLATE_ATTR_IMPL(inter_attr, const char *, const char *, AINTER_Type, AINTER_href, AINTER_value) 

TEMPLATE_ATTR_IMPL(qtlist_attr, u_long32, u_long32, AQTLIST_Type, AQTLIST_href, AQTLIST_value) 


TEMPLATE_ATTR_IMPL(strlist_attr, const char *, lList *, ASTRLIST_Type, ASTRLIST_href, ASTRLIST_value) 

TEMPLATE_ATTR_IMPL(usrlist_attr, const char *, lList *, AUSRLIST_Type, AUSRLIST_href, AUSRLIST_value) 

TEMPLATE_ATTR_IMPL(prjlist_attr, const char *, lList *, APRJLIST_Type, APRJLIST_href, APRJLIST_value) 

TEMPLATE_ATTR_IMPL(celist_attr, const char *, lList *, ACELIST_Type, ACELIST_href, ACELIST_value) 

TEMPLATE_ATTR_IMPL(solist_attr, const char *, lList *, ASOLIST_Type, ASOLIST_href, ASOLIST_value) 


