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

#include "sge.h"
#include "sgermon.h"
#include "cull.h"
#include "sge_unistd.h"
#include "sge_dstring.h"
#include "sge_log.h"

#include "sge_answer.h"

#include "msg_sgeobjlib.h"

#define ANSWER_LAYER CULL_LAYER

/****** sgeobj/answer/-AnswerList *********************************************
*  NAME
*     AnswerList - Object used to return errors/warning/infos
*
*  FUNCTION
*     Answer elements and lists are used to exchange information
*     about the level of success of an operation between caller
*     and callee. Especially in GDI answer element and lists are
*     used to transfer error information from server components
*     to clients.
*
*     Example:
*
*        void caller(void) {
*           lList *answer_list = NULL; 
*
*           callee(&answer_list);
*           if (answer_list_has_error(&answer_list)) {
*              try_to_handle_error();
*              answer_list = lFreeList(answer_list);
*           }
*        } 
*
*        void callee(lList **answer_list) {
*           char *s = malloc(256);
*
*           if (s == NULL) {
*              answer_list_add(answer_list, "no memory", 
*                              STATUS_ERROR, ANSWER_QUALITY_ERROR);
*              return;
*           }
*        }
*
*  SEE ALSO
*     sgeobj/answer/answer_has_quality() 
*     sgeobj/answer/answer_is_recoverable() 
*     sgeobj/answer/answer_exit_if_not_recoverable() 
*     sgeobj/answer/answer_get_quality_text() 
*     sgeobj/answer/answer_get_status() 
*     sgeobj/answer/answer_print_text() 
*     sgeobj/answer/answer_list_add() 
*     sgeobj/answer/answer_list_add_sprintf() 
*     sgeobj/answer/answer_list_has_quality() 
*     sgeobj/answer/answer_list_has_error() 
*     sgeobj/answer/answer_list_on_error_print_or_exit() 
*
*  NOTES
*     MT-NOTE: the answer list module is MT safe
******************************************************************************/

/****** sgeobj/answer/answer_has_quality() ************************************
*  NAME
*     answer_has_quality() -- Check for certain answer quality 
*
*  SYNOPSIS
*     bool answer_has_quality(const lListElem *answer, 
*                            answer_quality_t quality) 
*
*  FUNCTION
*     Return true (1) if "answer" has the given "quality" 
*
*  INPUTS
*     const lListElem *answer  - AN_Type element 
*     answer_quality_t quality - Quality id 
*
*  RESULT
*     bool - true or false 
*
*  NOTES
*     MT-NOTE: answer_has_quality() is MT safe
*******************************************************************************/
bool answer_has_quality(const lListElem *answer, answer_quality_t quality) 
{
   bool ret;

   DENTER(ANSWER_LAYER, "answer_has_quality");
   ret = (lGetUlong(answer, AN_quality) ==  quality) ? true : false;
   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_is_recoverable() *********************************
*  NAME
*     answer_is_recoverable() -- Check for recoverable error 
*
*  SYNOPSIS
*     int answer_is_recoverable(const lListElem *answer) 
*
*  FUNCTION
*     This function return true (1) if "answer" is an error where the
*     calling function application may recover from.
*     Following error are handeled as nonrecoverable:
*
*        STATUS_NOQMASTER
*        STATUS_NOCOMMD
*        STATUS_ENOKEY 
*
*  INPUTS
*     const lListElem *answer - AN_Type element 
*
*  RESULT
*     int - true or false
*
*  NOTES
*     MT-NOTE: answer_is_recoverable() is MT safe
******************************************************************************/
bool answer_is_recoverable(const lListElem *answer)
{
   bool ret = true;

   DENTER(ANSWER_LAYER, "answer_is_recoverable");
   if (answer != NULL) {
      const int max_non_recoverable = 4;
      const u_long32 non_recoverable[] = {
         STATUS_NOQMASTER,
         STATUS_NOCOMMD,
         STATUS_ENOKEY,
         STATUS_NOCONFIG
      };
      u_long32 status = lGetUlong(answer, AN_status);
      int i;

      for (i = 0; i < max_non_recoverable; i++) {
         if (status == non_recoverable[i]) {
            ret = false;
            break;
         }
      }
   }
   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_exit_if_not_recoverable() ************************
*  NAME
*     answer_exit_if_not_recoverable() -- Exit on certain errors 
*
*  SYNOPSIS
*     void answer_exit_if_not_recoverable(const lListElem *answer) 
*
*  FUNCTION
*     This function checks if "answer" is an unrecoverable error.
*     (answer_is_recoverable() is used to check this.) The error
*     text will then be printed to stderr and the calling
*     process will be terminated with exit code 1. 
*
*  INPUTS
*     const lListElem *answer - AN_Type element 
*
*  NOTES
*     This function may never return. 
*
*  NOTES
*     MT-NOTE: answer_exit_if_not_recoverable() is MT safe
******************************************************************************/
void answer_exit_if_not_recoverable(const lListElem *answer)
{
   DENTER(ANSWER_LAYER, "answer_exit_if_not_recoverable");
   if (!answer_is_recoverable(answer)) {
      fprintf(stderr, "%s: %s", answer_get_quality_text(answer),
              lGetString(answer, AN_text));
      DEXIT;
      SGE_EXIT(1);
   }
}

/****** sgeobj/answer/answer_get_quality_text() *******************************
*  NAME
*     answer_get_quality_text() -- Get quality text 
*
*  SYNOPSIS
*     const char* answer_get_quality_text(const lListElem *answer) 
*
*  FUNCTION
*     Returns a string representation for the quality of the "answer" 
*
*  INPUTS
*     const lListElem *answer - AN_Type list 
*
*  RESULT
*     const char* - String
*
*  NOTES
*     MT-NOTE: answer_get_quality_text() is MT safe
******************************************************************************/
const char *answer_get_quality_text(const lListElem *answer) 
{
   const char *quality_text[] = {
      "ERROR",
      "WARNING",
      "INFO"
   };
   u_long32 quality;

   DENTER(ANSWER_LAYER, "answer_get_quality_text");
   quality = lGetUlong(answer, AN_quality);
   if (quality > 2) {
      quality = 0;
   }
   DEXIT;
   return quality_text[quality];
}

/****** sgeobj/answer/answer_get_status() *************************************
*  NAME
*     answer_get_status() -- Return the error status.
*
*  SYNOPSIS
*     u_long32 answer_get_status(const lListElem *answer) 
*
*  FUNCTION
*     Return the error status of "answer". 
*
*  INPUTS
*     const lListElem *answer - AN_Type element 
*
*  RESULT
*     u_long32 - error status
*
*  NOTES
*     MT-NOTE: answer_get_status() is MT safe
******************************************************************************/
u_long32 answer_get_status(const lListElem *answer) 
{
   u_long32 ret;

   DENTER(ANSWER_LAYER, "answer_get_status");
   ret = lGetUlong(answer, AN_status);
   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_print_text() *************************************
*  NAME
*     answer_print_text() -- Prints error text 
*
*  SYNOPSIS
*     void answer_print_text(const lListElem *answer, 
*                            FILE *stream
*                            const char *prefix,
*                            const char *suffix) 
*
*  FUNCTION
*     Prints "prefix", the error text of "answer" and "suffix" 
*     to "stream".
*
*  INPUTS
*     const lListElem *answer - AN_Type element 
*     FILE *stream            - Output stream 
*     const char *prefix      - Introductional message
*     const char *prefix      - Final message
*
*  NOTES
*     MT-NOTE: answer_print_text() is MT safe
******************************************************************************/
void answer_print_text(const lListElem *answer, 
                       FILE *stream, 
                       const char *prefix,
                       const char *suffix)
{
   const char *text = NULL;

   DENTER(ANSWER_LAYER, "answer_print_text");
   text = lGetString(answer, AN_text);

   if (prefix != NULL) {
      fprintf(stream, "%s", prefix);
   }
   if (text != NULL) {
      fprintf(stream, "%s", text);
   }
   if (suffix != NULL) {
      fprintf(stream, "%s", suffix);
   }
   DEXIT;
}

/****** sgeobj/answer/answer_to_dstring() ************************************
*  NAME
*     answer_to_dstring() -- Copy answer to dstring without newline
*
*  SYNOPSIS
*     void answer_to_dstring(const lListElem *answer, dstring *diag) 
*
*  FUNCTION
*     Copy answer text into dstring without newline character.
*
*  INPUTS
*     const lListElem *answer - AN_Type element
*
*  OUTPUT
*     dstring *diag           - destination dstring
*
*  RESULT
*     void - 
*
*  NOTES
*     MT-NOTE: answer_to_dstring() is MT safe
*******************************************************************************/
void answer_to_dstring(const lListElem *answer, dstring *diag)
{
   if (diag) {
      if (!answer) {
         sge_dstring_copy_string(diag, MSG_ANSWERWITHOUTDIAG);
      } else {
         const char *s, *t;
         s = lGetString(answer, AN_text);
         if ((t=strchr(s, '\n'))) {
            sge_dstring_sprintf_append(diag, "%.*s", t-s, s); 
         }
         else {
            sge_dstring_append(diag, s);
         }   
      }
   }
}

/****** sgeobj/answer/answer_list_to_dstring() *********************************
*  NAME
*     answer_list_to_dstring() -- Copy answer to dstring without newline
*
*  SYNOPSIS
*     void answer_list_to_dstring(const lList *alp, dstring *diag) 
*
*  FUNCTION
*     Copy answer list text into dstring with each element separated by a
*     newline character.
*
*  INPUTS
*     const lList *alp        - AN_Type list
*     dstring *diag           - destination dstring
*
*  RESULT
*     void - 
*
*  NOTES
*     MT-NOTE: answer_list_to_dstring() is MT safe
*******************************************************************************/
void answer_list_to_dstring(const lList *alp, dstring *diag)
{
   if (diag) {
      if (!alp || (lGetNumberOfElem (alp) == 0)) {
         sge_dstring_copy_string(diag, MSG_ANSWERWITHOUTDIAG);
      } else {
         lListElem *aep = NULL;
         
         sge_dstring_clear (diag);
         
         for_each (aep, alp) {
            const char *s;

            s = lGetString(aep, AN_text);
            sge_dstring_append (diag, s);

            if (strchr(s, '\n') == NULL) {
               sge_dstring_append_char (diag, '\n');
            }
         }
      }
   }
}

/****** sgeobj/answer/answer_list_add_sprintf() *******************************
*  NAME
*     answer_list_add_sprintf() -- Format add an answer to an answer list
*
*  SYNOPSIS
*     bool answer_list_add_sprintf(lList **answer_list, u_long32 status, 
*                                  answer_quality_t quality, const char *fmt, 
*                                  ...) 
*
*  FUNCTION
*     This function creates a new answer element having the properties
*     "status", "quality", and a message text created from fmt and the 
*     following variable argument list.
*
*     The new element will be appended to "answer_list".
*     
*     If "answer_list" is NULL, no action is performed.
*
*     If the list pointed to by "answer_list" is NULL, a new list will be
*     created.
*
*  INPUTS
*     lList **answer_list      - AN_Type list
*     u_long32 status          - answer status
*     answer_quality_t quality - answer quality
*     const char *fmt          - format string to create message (printf)
*     ...                      - arguments used for formatting message
*
*  RESULT
*     bool - true on success, else false
*
*  SEE ALSO
*     sgeobj/answer/answer_list_add()
*
*  NOTES
*     MT-NOTE: answer_list_add_sprintf() is MT safe
******************************************************************************/
bool 
answer_list_add_sprintf(lList **answer_list, u_long32 status, 
                        answer_quality_t quality, const char *fmt, ...)
{
   bool ret = false;

   DENTER(ANSWER_LAYER, "answer_list_add");
   if (answer_list != NULL) {
      dstring buffer = DSTRING_INIT;
      const char *message;
      va_list ap;

      va_start(ap, fmt);
      message = sge_dstring_vsprintf(&buffer, fmt, ap);

      if (message != NULL) {
         ret = answer_list_add(answer_list, message, status, quality);
      }

      sge_dstring_free(&buffer);
   }

   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_list_has_quality() *******************************
*  NAME
*     answer_list_has_quality() -- Contains list  
*
*  SYNOPSIS
*     int answer_list_has_quality(lList **answer_list, 
*                                 answer_quality_t quality) 
*
*  FUNCTION
*     The function returns true (1) if the "answer_list" containes
*     at least one answer element with the given "quality". 
*
*  INPUTS
*     lList **answer_list      - AN_Type list 
*     answer_quality_t quality - quality value 
*
*  RESULT
*     bool - true or false
*
*  NOTES
*     MT-NOTE: answer_list_has_quality() is MT safe
******************************************************************************/
bool answer_list_has_quality(lList **answer_list, answer_quality_t quality)
{
   bool ret = false;

   DENTER(ANSWER_LAYER, "answer_list_has_quality");
   if (answer_list != NULL) {
      lListElem *answer;   /* AN_Type */

      for_each(answer, *answer_list) {
         if (answer_has_quality(answer, quality)) {
            ret = true;
            break;
         }
      }
   }
   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_list_has_error() *********************************
*  NAME
*     answer_list_has_error() -- Is an "error " in the list 
*
*  SYNOPSIS
*     bool answer_list_has_error(lList **answer_list) 
*
*  FUNCTION
*     The function returns true (1) if the "answer_list" containes
*     at least one error answer element 
*
*  INPUTS
*     lList **answer_list - AN_Type list 
*
*  RESULT
*     bool - true or false
*
*  NOTES
*     MT-NOTE: answer_list_has_error() is MT safe
******************************************************************************/
bool answer_list_has_error(lList **answer_list)
{
   bool ret;

   DENTER(ANSWER_LAYER, "answer_list_has_error");
   ret = answer_list_has_quality(answer_list, ANSWER_QUALITY_ERROR);
   DEXIT;
   return ret;
}               

/****** sgeobj/answer/answer_list_on_error_print_or_exit() ********************
*  NAME
*     answer_list_on_error_print_or_exit() -- Print and/or exit 
*
*  SYNOPSIS
*     void answer_list_on_error_print_or_exit(lList **answer_list, 
*                                             FILE *stream) 
*
*  FUNCTION
*     The error texts of all answer elements within "answer_list" are
*     printed to "stream". If an unrecoverable error is detected
*     the calling process will be terminated. 
*
*  INPUTS
*     lList **answer_list - AN_Type list 
*     FILE *stream        - output stream 
*
*  NOTES
*     MT-NOTE: answer_list_on_error_print_or_exit() is MT safe
******************************************************************************/
void answer_list_on_error_print_or_exit(lList **answer_list, FILE *stream)
{
   lListElem *answer;   /* AN_Type */

   DENTER(ANSWER_LAYER, "answer_list_on_error_print_or_exit");
   for_each(answer, *answer_list) {
      answer_exit_if_not_recoverable(answer);
      answer_print_text(answer, stream, NULL, NULL);
   }
   DEXIT;
}

/****** sgeobj/answer/answer_list_print_err_warn() ****************************
*  NAME
*     answer_list_print_err_warn() -- Print and/or exit 
*
*  SYNOPSIS
*     int answer_list_print_err_warn(lList **answer_list, 
*                                    const char *err_prefix, 
*                                    const char *warn_prefix) 
*
*  FUNCTION
*     Prints all messages contained in "answer_list". All error
*     messages will be printed to stderr with an an initial "err_prefix".
*     All warning and info messages will be printed to stdout with
*     the prefix "warn_prefix". 
*
*     If the "answer_list" contains at least one error then this
*     function will return with a positive return value. This value
*     is the errror status of the first error message.
*
*     If there is no error contained in 'answer_list' than this function 
*     will return with a value of 0. 
*
*     "*answer_list" will be freed.
*
*  INPUTS
*     lList **answer_list     - AN_Type list 
*     const char *err_prefix  - e.g. "qsub: "
*     const char *warn_prefix - e.g. MSG_WARNING
*
*  NOTES
*     MT-NOTE: answer_list_print_err_warn() is MT safe
******************************************************************************/
int answer_list_print_err_warn(lList **answer_list, 
                               const char *err_prefix,
                               const char *warn_prefix)
{
   int do_exit = 0;
   lListElem *answer;   /* AN_Type */
   u_long32 status = 0;

   DENTER(ANSWER_LAYER, "answer_list_print_err_warn");
   for_each(answer, *answer_list) {
      if (answer_has_quality(answer, ANSWER_QUALITY_ERROR)) {
         answer_print_text(answer, stderr, err_prefix, NULL);
         if (do_exit == 0) {
            status = answer_get_status(answer);
            do_exit = 1;
         }
      } else if (answer_has_quality (answer, ANSWER_QUALITY_WARNING)) {
         answer_print_text(answer, stdout, warn_prefix, NULL);
      }
      else {
         answer_print_text(answer, stdout, NULL, NULL);
      }
   }
   *answer_list = lFreeList(*answer_list);
   DEXIT;
   return (int)status;
}

/****** sgeobj/answer/answer_list_handle_request_answer_list() ****************
*  NAME
*     answer_list_handle_request_answer_list() -- handle res. of request
*
*  SYNOPSIS
*     int answer_list_handle_request_answer_list(lList **answer_list, 
*                                                FILE *stream) 
*
*  FUNCTION
*     Processes the answer list that results from a gdi request
*     (sge_gdi or sge_gdi_multi).
*     Outputs and errors and warnings and returns the first error
*     or warning status code.
*     The answer list is freed.
*
*  INPUTS
*     lList **answer_list - answer list to process
*     FILE *stream        - output stream
*
*  RESULT
*     int - first error or warning status code or STATUS_OK
*
*  NOTES
*     MT-NOTE: answer_list_handle_request_answer_list() is MT safe
******************************************************************************/
int answer_list_handle_request_answer_list(lList **answer_list, FILE *stream) {
   int ret = STATUS_OK;

   DENTER(ANSWER_LAYER, "answer_list_handle_request_answer_list");
   if(answer_list != NULL && *answer_list != NULL) {
      lListElem *answer;

      for_each(answer, *answer_list) {
         if(answer_has_quality(answer, ANSWER_QUALITY_ERROR) ||
            answer_has_quality(answer, ANSWER_QUALITY_WARNING)) {
            answer_print_text(answer, stream, NULL, NULL);
            if(ret == STATUS_OK) {
               ret = lGetUlong(answer, AN_status);
            }
         }
      }
      *answer_list = lFreeList(*answer_list);
   } else {
      fprintf(stream, MSG_ANSWER_NOANSWERLIST);
      return STATUS_EUNKNOWN;
   }
   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_list_add() ***************************************
*  NAME
*     answer_list_add() -- Add an answer to an answer list
*
*  SYNOPSIS
*     int answer_list_add(lList **answer_list,
*                         const char *text,
*                         u_long32 status,
*                         answer_quality_t quality)
*
*  FUNCTION
*     This function creates a new answer element (using "quality",
*     "status" and "text"). The new element will be appended to
*     "answer_list"
*
*     If "answer_list" is NULL, no action is performed.
*
*     If the list pointed to by "answer_list" is NULL, a new list will be
*     created.
*
*  INPUTS
*     lList **answer_list      - AN_Type list
*     const char *text         - answer text
*     u_long32 status          - answer status
*     answer_quality_t quality - answer quality
*
*  RESULT
*     int - error state
*        true  - OK
*        false - error occured
*
*  SEE ALSO
*     sgeobj/answer/answer_list_add_sprintf()
*
*  NOTES
*     MT-NOTE: answer_list_add() is MT safe
******************************************************************************/
bool
answer_list_add(lList **answer_list, const char *text,
                u_long32 status, answer_quality_t quality)
{
   int ret = false;

   DENTER(ANSWER_LAYER, "answer_list_add");

   if (answer_list != NULL) {
      lListElem *answer = lCreateElem(AN_Type);

      if (answer != NULL) {
         lSetString(answer, AN_text, text);
         lSetUlong(answer, AN_status, status);
         lSetUlong(answer, AN_quality, quality);

         if (*answer_list == NULL) {
            *answer_list = lCreateList("", AN_Type);
         }

         if (*answer_list != NULL) {
            lAppendElem(*answer_list, answer);
            ret = true;
         }
      }

      if (!ret) {
         answer = lFreeElem(answer);
      }
   }
   DEXIT;
   return ret;
}

bool answer_list_add_elem(lList **answer_list, lListElem *answer)
{
   int ret = false;

   DENTER(ANSWER_LAYER, "answer_list_add_elem");
   if (answer_list != NULL) {
      if (*answer_list == NULL) {
         *answer_list = lCreateList("", AN_Type);
      }
      if (*answer_list != NULL) {
         lAppendElem(*answer_list, answer);
         ret = true;
      }
   }
   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_list_replace() ***********************************
*  NAME
*     answer_list_replace() -- repalce a answer list 
*
*  SYNOPSIS
*     void answer_list_replace(lList **answer_list, lList **new_list) 
*
*  FUNCTION
*     free *answer_list and replace it by *new_list. 
*
*  INPUTS
*     lList **answer_list - AN_Type 
*     lList **new_list    - AN_Type 
*
*  RESULT
*     void - none 
*
*  NOTES
*     MT-NOTE: answer_list_replace() is MT safe
*******************************************************************************/
void answer_list_replace(lList **answer_list, lList **new_list)
{
   DENTER(ANSWER_LAYER, "answer_list_replace");
   if (answer_list != NULL) {
      *answer_list = lFreeList(*answer_list);

      if (new_list != NULL) {
         *answer_list = *new_list; 
         *new_list = NULL;
      } else {
         *answer_list = NULL; 
      }
   }
   DEXIT;
}

/****** sgeobj/answer/answer_list_append_list() *******************************
*  NAME
*     answer_list_append_list() -- Append two lists 
*
*  SYNOPSIS
*     void answer_list_append_list(lList **answer_list, lList **new_list) 
*
*  FUNCTION
*     Append "new_list" after "answer_list". *new_list will be NULL afterwards 
*
*  INPUTS
*     lList **answer_list - AN_Type list 
*     lList **new_list    - AN_Type list 
*
*  RESULT
*     void - None 
*******************************************************************************/
void answer_list_append_list(lList **answer_list, lList **new_list)
{
   DENTER(ANSWER_LAYER, "answer_list_append_list");
   if (answer_list != NULL && new_list != NULL) {
      if (*answer_list == NULL && *new_list != NULL) {
         *answer_list = lCreateList("", AN_Type);
      }
      if (*new_list != NULL) {
         lAddList(*answer_list, *new_list);
         *new_list = NULL;
      }
   }
   DEXIT;
}

/****** sgeobj/answer/answer_list_log() ****************************
*  NAME
*     answer_list_log() -- output and free answer_list
*
*  SYNOPSIS
*     bool
*     answer_list_log(lList **answer_list, bool is_free_list)
*
*  FUNCTION
*     Prints all messages contained in "answer_list". 
*     The ERROR, WARNING and INFO macros will be used for output.
*
*     If the "answer_list" contains at least one error then this
*     function will return true.
*
*     If there is no error contained in 'answer_list' then this function 
*     will return with a value of false. 
*
*     "*answer_list" will only be freed and set to NULL, if is_free_list is
*     true.
*
*  INPUTS
*     lList **answer_list     - AN_Type list 
*     bool is_free_list       - if true, frees the answer list
*
*  NOTES
*     MT-NOTE: answer_list_print_err_warn() is MT safe
******************************************************************************/
bool answer_list_log(lList **answer_list, bool is_free_list) {

   bool ret = false;
   lListElem *answer;   /* AN_Type */

   DENTER(ANSWER_LAYER, "answer_list_output");

   if (answer_list != NULL && *answer_list != NULL) {
      for_each(answer, *answer_list) {
         switch (lGetUlong(answer, AN_quality)) {
            case ANSWER_QUALITY_ERROR:
               ERROR((SGE_EVENT, lGetString(answer, AN_text)));
               ret = true;
               break;
            case ANSWER_QUALITY_WARNING:
               WARNING((SGE_EVENT, lGetString(answer, AN_text)));
               break;
            case ANSWER_QUALITY_INFO:
               INFO((SGE_EVENT, lGetString(answer, AN_text)));
               break;
            default:
               break;
         }
      }
      if (is_free_list) {
         *answer_list = lFreeList(*answer_list);
      }
   }

   DEXIT;
   return ret;
}

/****** sgeobj/answer/answer_list_output() ****************************
*  NAME
*     answer_list_output() -- output and free answer_list
*
*  SYNOPSIS
*     bool
*     answer_list_output(lList **answer_list)
*
*  FUNCTION
*     Prints all messages contained in "answer_list". 
*     The ERROR, WARNING and INFO macros will be used for output.
*
*     If the "answer_list" contains at least one error then this
*     function will return true.
*
*     If there is no error contained in 'answer_list' then this function 
*     will return with a value of false. 
*
*     "*answer_list" will be freed and set to NULL.
*
*  INPUTS
*     lList **answer_list     - AN_Type list 
*
*  NOTES
*     MT-NOTE: answer_list_output() is MT safe
******************************************************************************/

bool answer_list_output(lList **answer_list) {
   return answer_list_log(answer_list, true);
}
