/*___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 "rmon/sgermon.h" 

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

#include "comm/commlib.h"

#include "basis_types.h"
#include "sge_answer.h"
#include "sge_str.h"
#include "sge_href.h"
#include "sge_hgroup.h"

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

#define HOSTREF_LAYER BASIS_LAYER

/****** sgeobj/href/href_list_add() *******************************************
*  NAME
*     href_list_add() -- Add host or hostgroup reference.
*
*  SYNOPSIS
*     bool 
*     href_list_add(lList **this_list, lList **answer_list, 
*                   const char *host_or_group) 
*
*  FUNCTION
*     Add a host or hostgroup given by 'host_or_group' into the list 
*     'this_list'. If the function is successfull then the function
*     returns 'true' otherwise it will add an entry into 'answer_list'
*     and return with 'false'. If 'this_list' does not exist than it
*     will be created.
*
*  INPUTS
*     lList **this_list         - HR_Type list 
*     lList **answer_list       - AN_Type list 
*     const char *host_or_group - host or group name  
*
*  RESULT
*     bool - error state
*        true - Success
*        false - Error
*******************************************************************************/
bool 
href_list_add(lList **this_list, lList **answer_list, const char *host_or_group)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_add");
   if (this_list != NULL && host_or_group != NULL) {
      if (!href_list_has_member(*this_list, host_or_group)) {
         lListElem *h_or_g;   /* HR_Type */

         h_or_g = lAddElemHost(this_list, HR_name, host_or_group, HR_Type);
         if (h_or_g == NULL) {
            answer_list_add(answer_list, MSG_GDI_OUTOFMEMORY,
                            STATUS_EMALLOC, ANSWER_QUALITY_ERROR);
            ret = false;
         }
      }
   } 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;
}

/****** sgeobj/href/href_list_has_member() ************************************
*  NAME
*     href_list_has_member() -- Is reference already in list 
*
*  SYNOPSIS
*     bool 
*     href_list_has_member(const lList *this_list, 
*                          const char *host_or_group) 
*
*  FUNCTION
*     Is the given host or hostgroup ('host_or_group') already
*     contained in the reference list?
*
*  INPUTS
*     const lList *this_list    - HR_Type list 
*     const char *host_or_group - hostname or hgroup 
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_has_member(const lList *this_list, const char *host_or_group)
{
   bool ret = false;

   DENTER(HOSTREF_LAYER, "href_list_has_member");
   if (this_list != NULL && host_or_group != NULL) {
      if (href_list_locate(this_list, host_or_group) != NULL) {
         ret = true;
      }
   } else {
      /*
       * If one of the argumets was not given by the callee we may be sure
       * that 'host_or_group' is not member of 'this_list'!
       */
      ;
   }
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_compare() ***************************************
*  NAME
*     href_list_compare() -- Finds additional entries in list 
*
*  SYNOPSIS
*     bool 
*     href_list_compare(const lList *this_list, lList **answer_list, 
*                       const lList *list, lList **add_hosts, 
*                       lList **add_groups, lList **equity_hosts,
*                       lList **equity_groups) 
*
*  FUNCTION
*     This function will find differences between two hostref lists
*     given by 'this_list' and 'list'. Hosts and hostgroups which are
*     only in 'this_list' can be found in 'add_hosts' and 'add_groups'.
*     References which are contained in both lists can be found in 
*     'equity_hosts' and 'equity_groups' after a call to this function.
*
*     If the calling function is not interested in one ore more of the
*     result lists than NULL should be used as parameter. The calling
*     function is responsible to free all result lists. 
*
*     If the callee is also interested in the references which are
*     only part of 'list' than this function can not be used.
*     href_list_find_diff() should be used in this case. 
*
*  INPUTS
*     const lList *this_list - HR_Type list to comapre
*     lList **answer_list    - AN_Type list 
*     const lList *list      - 2nd HR_Type list to be compared
*     lList **add_hosts      - HR_Type list 
*     lList **add_groups     - HR_Type list 
*     lList **equity_hosts   - HR_Type list
*     lList **equity_groups  - HR_Type list
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*
*  SEE ALSO
*     sgeobj/href/href_list_find_diff()
*******************************************************************************/
bool 
href_list_compare(const lList *this_list, lList **answer_list,
                  const lList *list, lList **add_hosts,
                  lList **add_groups, lList **equity_hosts, 
                  lList **equity_groups) 
{
   bool ret = true;
   lListElem *this_elem;   /* HR_Type */
  
   DENTER(HOSTREF_LAYER, "href_list_compare"); 

   for_each(this_elem, this_list) {
      const char *host_or_group = lGetHost(this_elem, HR_name);

      if (!href_list_has_member(list, host_or_group)) {
         if (is_hgroup_name(host_or_group)) {
            if (add_groups != NULL) {
               ret = href_list_add(add_groups, answer_list, host_or_group);
            }
         } else if (add_hosts != NULL) {
            ret = href_list_add(add_hosts, answer_list, host_or_group);
         }
      } else {
         if (is_hgroup_name(host_or_group)) {
            if (equity_groups != NULL) {
               ret = href_list_add(equity_groups, answer_list, host_or_group);
            }
         } else if (equity_hosts != NULL) {
            ret = href_list_add(equity_hosts, answer_list, host_or_group);
         }
      }
      if (!ret) {
         break;
      }
   }
   DRETURN(ret);
}

/****** sgeobj/href/href_list_find_diff() *************************************
*  NAME
*     href_list_find_diff() -- difference between two lists 
*
*  SYNOPSIS
*     bool 
*     href_list_find_diff(const lList *this_list, lList **answer_list, 
*                         const lList *list, lList **add_hosts, 
*                         lList **rem_hosts, lList **add_groups, 
*                         lList **rem_groups) 
*
*  FUNCTION
*     Will identify differences between 'this_list' and 'list'.
*     hosts which are only in 'this_list' will be copied into 'add_hosts'
*     hosts which are only in 'list' will be copied into 'rem_hosts'
*     groups which are only in 'this_list' will be copied to 'add_groups'
*     groups which are only in 'this' will be copied to 'rem_groups'
*
*     The calling context is responsible to free all result lists.
*     If the callee is not interested in one or more of the resultlist
*     than NULL should be used as parameter for this function.
*
*  INPUTS
*     const lList *this_list - HR_Type list to be compared
*     lList **answer_list    - AN_Type list 
*     const lList *list      - 2nd HR_Type list to be compared
*     lList **add_hosts      - HR_Type list 
*     lList **rem_hosts      - HR_Type list 
*     lList **add_groups     - HR_Type list 
*     lList **rem_groups     - HR_Type list 
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*
*  SEE ALSO
*     sgeobj/href/href_list_find_diff()
*******************************************************************************/
bool 
href_list_find_diff(const lList *this_list, lList **answer_list,
                    const lList *list, lList **add_hosts,
                    lList **rem_hosts, lList **add_groups, lList **rem_groups) 
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_find_diff");
   ret &= href_list_compare(this_list, answer_list, list,
                            add_hosts, add_groups, NULL, NULL);
   ret &= href_list_compare(list, answer_list, this_list,
                               rem_hosts, rem_groups, NULL, NULL);
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_find_effective_diff() ***************************
*  NAME
*     href_list_find_effective_diff() -- Resolves groups and creates diff
*
*  SYNOPSIS
*     bool href_list_find_effective_diff(lList **answer_list, 
*                                        const lList *add_groups, 
*                                        const lList *rem_groups, 
*                                        const lList *master_list, 
*                                        lList **add_hosts, lList **rem_hosts) 
*
*  FUNCTION
*     Resolves host names of all groups contained in "add_groups"
*     and "rem_groups". Hostnames not part of both resulting hostgroup
*     sets will be stored in "add_hosts" and "rem_hosts".
*
*  INPUTS
*     lList **answer_list      - AN_Type list 
*     const lList *add_groups  - HR_Type list1 (hgroups)
*     const lList *rem_groups  - HR_Type list2 (hgroups)
*     const lList *master_list - HGRP_Type list of all hgroups
*     lList **add_hosts        - resolved "add_groups" hosts not part
*                                of "rem_groups" 
*     lList **rem_hosts        - resolved "rem_groups" hosts not part
*                                of "add_hosts"
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
******************************************************************************/
bool
href_list_find_effective_diff(lList **answer_list, const lList *add_groups, 
                              const lList *rem_groups, const lList *master_list,
                              lList **add_hosts, lList **rem_hosts)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_find_effective_diff");
   if (ret && add_groups != NULL) {
      ret &= href_list_find_all_references(add_groups, answer_list,
                                           master_list, add_hosts, NULL);
   }
   if (ret && rem_groups != NULL) {
      ret &= href_list_find_all_references(rem_groups, answer_list,
                                           master_list, rem_hosts, NULL);
   }
   if (ret && add_hosts != NULL && *add_hosts != NULL &&
       rem_hosts != NULL && *rem_hosts != NULL) {
      lList *tmp_rem_hosts = NULL;
      lList *tmp_add_hosts = NULL;

      ret &= href_list_find_diff(*add_hosts, answer_list,
                                 *rem_hosts, &tmp_add_hosts,
                                 &tmp_rem_hosts, NULL, NULL);
      if (ret) {
         lFreeList(add_hosts);
         lFreeList(rem_hosts);
         *add_hosts = tmp_add_hosts;
         *rem_hosts = tmp_rem_hosts;
         tmp_add_hosts = NULL;
         tmp_rem_hosts = NULL;
      }
   }
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_locate() ****************************************
*  NAME
*     href_list_locate() -- Find an entry in the reference list 
*
*  SYNOPSIS
*     lListElem* 
*     href_list_locate(const lList *this_list, const char *name) 
*
*  FUNCTION
*     Find an entry in the reference list. 
*
*  INPUTS
*     const lList *this_list - HR_Type 
*     const char *name       - host or groupname 
*
*  RESULT
*     lListElem* - Pointer to host or hostgroup element or NULL 
*******************************************************************************/
lListElem *
href_list_locate(const lList *this_list, const char *name) 
{
   lListElem *ret = NULL;  /* HR_Type */

   DENTER(HOSTREF_LAYER, "href_list_locate");
   if (this_list != NULL && name != NULL) {
      ret = lGetElemHost(this_list, HR_name, name);
   }
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_find_references() *******************************
*  NAME
*     href_list_find_references() -- Find referenced hosts and groups 
*
*  SYNOPSIS
*     bool 
*     href_list_find_references(const lList *this_list, 
*                               lList **answer_list, 
*                               const lList *master_list, 
*                               lList **referenced_hosts, 
*                               lList **referenced_groups) 
*
*  FUNCTION
*     Finds hosts and hostgroups, which are directy referenced
*     in the hostgroups mentioned in 'this_list'. 'master_list' is
*     the list of all existing hostgroups. Directly referenced hosts
*     and hostgroups will be added to 'used_hosts' and 'used_groups'.
*     In case of any errors 'answer_list' will be filled.
*
*  INPUTS
*     const lList *this_list   - HR_Type 
*     lList **answer_list      - AN_Type 
*     const lList *master_list - HGRP_Type
*     lList **used_hosts       - HR_Type 
*     lList **used_groups      - HR_Type 
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_find_references(const lList *this_list, lList **answer_list,
                          const lList *master_list, lList **used_hosts,
                          lList **used_groups)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_find_references");
   if (this_list != NULL && master_list != NULL) { 
      lListElem *href;  /* HR_Type */

      /*
       * Handle each reference which was given by the calling context
       */
      for_each(href, this_list) {
         const char *name = lGetHost(href, HR_name);
         bool is_group = is_hgroup_name(name);
         lListElem *hgroup = NULL;  /* HGRP_name */

         /*
          * Try to locate the concerned hgroup object
          * or add host
          */
         if (is_group) {
            hgroup = hgroup_list_locate(master_list, name);
         } else {
            if (used_hosts != NULL) {
               href_list_add(used_hosts, answer_list, name);
            }
         }

         if (hgroup != NULL) {
            lList *href_list2 = lGetList(hgroup, HGRP_host_list);
            lListElem *href2;    /* HR_Type */

            /* 
             * Add each element contained in the sublist of the hostgroup
             * we found previously to one of the result lists.
             */
            for_each(href2, href_list2) {
               const char *name2 = lGetHost(href2, HR_name);

               if (is_hgroup_name(name2)) {
                  if (used_groups != NULL) {
                     href_list_add(used_groups, answer_list, name2); 
                  }
               } else {
                  if (used_hosts != NULL) {
                     href_list_add(used_hosts, answer_list, name2); 
                  }
               }   
            }
         }
      } 
   } 
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_find_all_references() ***************************
*  NAME
*     href_list_find_all_references() -- Find referenced hosts and hgroups 
*
*  SYNOPSIS
*     bool 
*     href_list_find_all_references(const lList *this_list, 
*                                   lList **answer_list, 
*                                   const lList *master_list, 
*                                   lList **used_hosts, 
*                                   lList **used_groups) 
*
*  FUNCTION
*     Finds all hosts and hostgroups which are directly and inderectly
*     referenced by the hostgroups mentioned in 'this_list'. 
*     'master_list' is the list of all existing hostgroups. Referenced 
*     hosts and hostgroups will be added to 'used_hosts' and 'used_groups'.
*     In case of any errors 'answer_list' will be filled.
*
*  INPUTS
*     const lList *this_list   - RN_Type 
*     lList **answer_list      - AN_Type 
*     const lList *master_list - HGRP_Type 
*     lList **used_hosts       - RN_Type 
*     lList **used_groups      - RN_Type 
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_find_all_references(const lList *this_list, lList **answer_list,
                              const lList *master_list, lList **used_hosts,
                              lList **used_groups)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_find_all_references");
   if (this_list != NULL && master_list != NULL) {
      lList *tmp_used_groups = NULL;
      bool free_tmp_list = false;

      if (used_groups == NULL) {
         used_groups = &tmp_used_groups;
         free_tmp_list = true;
      }

      /*
       * Find all direct referenced hgroups and hosts
       */
      ret &= href_list_find_references(this_list, answer_list, master_list,
                                       used_hosts, used_groups);

      /* 
       * If there are subgroups then try to find their direct referenced
       * groups and hosts, subgroups ...
       *
       * Recursive!
       */
      if (ret && used_groups != NULL && *used_groups != NULL) {
         lList *used_sub_groups = NULL;
         lList *used_sub_hosts = NULL;

         ret &= href_list_find_all_references(*used_groups, answer_list,
                                              master_list, &used_sub_hosts,
                                              &used_sub_groups);
         if (ret) {
            if (used_hosts != NULL && used_sub_hosts != NULL) {
               if (*used_hosts != NULL) {
                  lAddList(*used_hosts, &used_sub_hosts);
               } else {
                  *used_hosts = used_sub_hosts;
                  used_sub_hosts = NULL;
               }
            }
            if (used_groups != NULL && used_sub_groups != NULL) {
               if (*used_groups != NULL) {
                  lAddList(*used_groups, &used_sub_groups);
               } else {
                  *used_groups = used_sub_groups;
                  used_sub_groups = NULL;
               }
            }
         } 
      }

      if (free_tmp_list) {
         lFreeList(&tmp_used_groups);
      }
   } 
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_find_referencees() ******************************
*  NAME
*     href_list_find_referencees() --  Find occupying hosts and hgroups
*
*  SYNOPSIS
*     bool 
*     href_list_find_referencees(const lList *this_list, 
*                                lList **answer_list, 
*                                const lList *master_list, 
*                                lList **occupant_groups) 
*
*  FUNCTION
*     Finds hostgroup references which directy occupy at least one of the
*     hostgroups mentioned in 'this_list'. 'master_list' is the list of 
*     all existing hostgroups. Directly occupying hostgroups will be 
*     added to 'occupant_groups'. In case of any errors 'answer_list' 
*     will be filled.
*
*  INPUTS
*     const lList *this_list   - HR_Type 
*     lList **answer_list      - AN_Type 
*     const lList *master_list - HGRP_Type 
*     lList **occupant_groups  - HR_Type 
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_find_referencees(const lList *this_list, lList **answer_list,
                           const lList *master_list, lList **occupant_groups)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_find_referencees");
   if (this_list != NULL && occupant_groups != NULL) {
      lListElem *href;  /* HR_Type */

      for_each(href, this_list) {
         const char *name = lGetHost(href, HR_name);

         if (is_hgroup_name(name)) {
            lListElem *hgroup;   /* HGRP_Type */

            for_each(hgroup, master_list) {
               lList *href_list = lGetList(hgroup, HGRP_host_list);
               lListElem *href = href_list_locate(href_list, name);

               if (href != NULL) {
                  const char *name = lGetHost(hgroup, HGRP_name);

                  href_list_add(occupant_groups, answer_list, name);
               }
            }
         } 
      }
   } 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;
}

/****** sgeobj/href/href_list_find_all_referencees() **************************
*  NAME
*     href_list_find_all_referencees() -- Find occupying hosts and groups 
*
*  SYNOPSIS
*     bool 
*     href_list_find_all_referencees(const lList *this_list, 
*                                    lList **answer_list, 
*                                    const lList *master_list, 
*                                    lList **occupant_groups) 
*
*  FUNCTION
*     Finds recursivly all hostgroups which occupy the hostgroups mentioned in 
*     'this_list'. 'master_list' is the list of all
*     existing hostgroups. Occupying hostgroups will be
*     added to 'occupant_groups'. In case of any errors 'answer_list'
*     will be filled.
*
*  INPUTS
*     const lList *this_list   - RH_Type 
*     lList **answer_list      - AH_Type 
*     const lList *master_list - HGRP_Type 
*     lList **occupant_groups  - RH_Type 
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_find_all_referencees(const lList *this_list, lList **answer_list,
                               const lList *master_list, 
                               lList **occupant_groups)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_find_all_referencees");
   if (this_list != NULL && occupant_groups != NULL) {

      /*
       * Find parents of all given hgroups
       */
      ret &= href_list_find_referencees(this_list, answer_list,
                                        master_list, occupant_groups);

      if (*occupant_groups != NULL && ret) {
         lList *occupant_sub_groups = NULL;  /* HR_Type */

         /*
          * Find grandparents, ...
          *
          * Recursive!
          */
         ret &= href_list_find_all_referencees(*occupant_groups, answer_list,
                                               master_list, 
                                               &occupant_sub_groups);

         if (occupant_sub_groups != NULL && ret) {
            lAddList(*occupant_groups, &occupant_sub_groups);
            occupant_sub_groups = 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;
}

/****** sgeobj/href/href_list_resolve_hostnames() *****************************
*  NAME
*     href_list_resolve_hostnames() -- resolve hostnames 
*
*  SYNOPSIS
*     bool 
*     href_list_resolve_hostnames(lList *this_list, 
*                                 lList **answer_list, bool ignore_errors
*
*  FUNCTION
*     Resolve hostnames contained in 'this_list'. Depending on the
*     'ignore_errors' parameter the function will either fail if a
*     host is not resolvable or this will be ignored.
*
*  INPUTS
*     lList *this_list    - HR_Type list 
*     lList **answer_list - AN_Type list 
*     bool ignore_errors  - ignore if a host is not resolveable
*
*  RESULT
*     bool - error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_resolve_hostnames(lList *this_list, lList **answer_list,
                            bool ignore_errors) 
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_resolve_hostnames");
   if (this_list != NULL) {
      lListElem *href = NULL;

      for_each(href, this_list) {
         const char *name = lGetHost(href, HR_name);

         if (!is_hgroup_name(name)) {
            char resolved_name[CL_MAXHOSTLEN+1];
            int back = getuniquehostname(name, resolved_name, 0);

            if (back == CL_RETVAL_OK) {
               lSetHost(href, HR_name, resolved_name);
            } else {
               if (!ignore_errors) {
                  INFO((SGE_EVENT, MSG_HGRP_UNKNOWNHOST, name));
                  answer_list_add(answer_list, SGE_EVENT, 
                                  STATUS_ESYNTAX, ANSWER_QUALITY_ERROR);
                  ret = false;
               }
            }
         }
      }
   }
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_append_to_dstring() *****************************
*  NAME
*     href_list_append_to_dstring() -- Print href-list to dstring 
*
*  SYNOPSIS
*     bool 
*     href_list_append_to_dstring(const lList *this_list, 
*                                 dstring *string) 
*
*  FUNCTION
*     Print href-list to dstring 
*
*  INPUTS
*     const lList *this_list - HR_Type  
*     dstring *string        - dynamic string 
*
*  RESULT
*     bool - Error state
*        true  - Success
*        false - Error
*******************************************************************************/
bool 
href_list_append_to_dstring(const lList *this_list, dstring *string)
{
   const char *const delim = " ";
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_append_to_dstring");
   if (this_list != NULL && string != NULL) {
      lListElem *href;  /* HR_Type */
      bool is_first = true;

      for_each(href, this_list) {
         const char *name = lGetHost(href, HR_name);

         if (!is_first) {
            sge_dstring_append(string, delim);
         }
         sge_dstring_append(string, name);
         is_first = false; 
      }
   } else {
      ret = false;
   } 
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_remove_existing() *******************************
*  NAME
*     href_list_remove_existing() -- Removes entries from list 
*
*  SYNOPSIS
*     bool href_list_remove_existing(lList **this_list, 
*                                    lList **answer_list, 
*                                    lList *list) 
*
*  FUNCTION
*     Removes all entries contained in "list" will be removed from 
*     "this_list" if they exist.
*
*  INPUTS
*     lList **this_list   - HR_Type list 
*     lList **answer_list - AN_Type list 
*     lList *list         - HR_Type list 
*
*  RESULT
*     bool - Error state
*        true  - Success
*        false - Error
******************************************************************************/
bool
href_list_remove_existing(lList **this_list, lList **answer_list,
                          lList *list)
{
   bool ret = true;

   DENTER(HOSTREF_LAYER, "href_list_remove_existing");
   if (this_list != NULL && *this_list != NULL && list != NULL) {
      lListElem * href = NULL;

      for_each(href, list) {
         const char *hostname = lGetHost(href, HR_name);
         lListElem *existing_href = lGetElemHost(*this_list, HR_name, hostname);

         if (existing_href != NULL) {
            DTRACE;
            lRemoveElem(*this_list, &existing_href);
         }
      }
      if (lGetNumberOfElem(*this_list) == 0) {
         lFreeList(this_list);
      }
   }
   DEXIT;
   return ret;
}

/****** sgeobj/href/href_list_debug_print() ***********************************
*  NAME
*     href_list_debug_print() -- Prints HR_Type list into TOP_LAYER 
*
*  SYNOPSIS
*     void href_list_debug_print(const lList *this_list, const char *prefix) 
*
*  FUNCTION
*     Prints prefix and HR_Type "this_list" into TOP_LAYER of debug 
*     output if this_list exists. 
*
*  INPUTS
*     const lList *this_list - HR_Type list 
*     const char *prefix     - prefix string 
*
*  RESULT
*     void -  None
******************************************************************************/
void
href_list_debug_print(const lList *this_list, const char *prefix) 
{
   lListElem *href = NULL;
   dstring message = DSTRING_INIT;
   bool is_first_hostname = true;
   DENTER(TOP_LAYER, "href_list_debug_print");

   for_each(href, this_list) {
      const char *hostname = lGetHost(href, HR_name);

      if (is_first_hostname) {
         sge_dstring_copy_string(&message, prefix);
         is_first_hostname = false;
      } else {
         sge_dstring_append(&message, ", ");
      }
      sge_dstring_append(&message, hostname);
   }
   if (!is_first_hostname) {
      sge_dstring_append(&message, "\n");
      DPRINTF((sge_dstring_get_string(&message)));
   }
   sge_dstring_free(&message);
   DEXIT;
}

/****** sgeobj/href/href_list_make_uniq() *************************************
*  NAME
*     href_list_make_uniq() -- remove duplicates 
*
*  SYNOPSIS
*     void href_list_make_uniq(lList *this_list, lList **answer_list) 
*
*  FUNCTION
*     Remove duplicates from "this_list" 
*
*  INPUTS
*     lList *this_list    - HR_Type list 
*     lList **answer_list - AN_Type list 
*
*  RESULT
*     void - none 
*******************************************************************************/
void
href_list_make_uniq(lList *this_list, lList **answer_list)
{
   lListElem *elem = NULL;
   lListElem *next_elem = NULL;

   DENTER(TOP_LAYER, "href_list_make_uniq");
   next_elem = lFirst(this_list);
   while((elem = next_elem) != NULL) {
      lListElem *elem2 = NULL;
      const void *iterator = NULL;

      next_elem = lNext(elem);
      elem2 = lGetElemHostFirst(this_list, HR_name, 
                                lGetHost(elem, HR_name), &iterator); 
      if (elem2 != NULL && elem != elem2) {
         lRemoveElem(this_list, &elem);
      }
   }
   DEXIT;
}

