#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "al.h"

#include "msgP.h"

#define OR ||

/*****************************************************/
/* This file contains the code for the Msg (message) */
/* object, used in Athena Information Lens.          */
/* Written Brian R. Gardner, 1988.                   */
/*****************************************************/


/*****************************************************************************/
/**********************  A few trival functions. *****************************/
/*****************************************************************************/

int AlMsg_ptrcmp(msg1, msg2)
     VOIDP msg1, msg2;
 { /* Simple comparator for Registry_create to use. */
   /* Test if two Msg obs are in fact the same one. */
   /* --------------------------------------------- */
   return(((Msg) msg1 == (Msg) msg2));
 };              /* end of AlMsg_ptrcmp()  */

NORET AlMsg_append_darray(requestors_darray, the_darray)
     Darray requestors_darray, the_darray;
 {
   unsigned i, darray_len;

   darray_len = Darray_len(the_darray);
   for(i = 0; i < darray_len; i++)
     Darray_addh(requestors_darray, (VOIDP) Darray_get(the_darray, i));
 };              /* end of AlMsg_append_darray()  */
/****************************************************************************/

Bool AlMsg_strncmp(str1, str2, len)
     char *str1, *str2;
     int len;
 {  int i;

     for (i = 0; i < len; i++)
       if ((str1[i] == str2[i]) && (str1[i] != '\0'))
         {}
       else
         return(Bool_FALSE);

     return(Bool_TRUE);
 };              /* end of AlMsg_strncmp()  */

/*****************************************************************************/
/**************  MACROs :: A few trival macros for Bools. ********************/
/*****************************************************************************/

#define AlMsg_iscntrl(c)  (! ((c >= ' ') && (c <= '~')))

#define AlMsg_unprintable_char(c) \
   ((AlMsg_iscntrl((int) c) && (c != '\n')) \
     ? Bool_TRUE : Bool_FALSE)

#define AlMsg_Bool_to_str(b) ((b == Bool_FALSE) ? "Bool_FALSE" : "Bool_TRUE")

/*****************************************************************************/
/*************  MACROs :: Set States of a Message  ***************************/
/*****************************************************************************/

#define AlMsg_set_num_of_fields(the_msg, field_count) \
  (the_msg->number_of_fields = field_count)

#define AlMsg_set_msg_line_length(the_msg, line_count) \
  (the_msg->msg_line_length = line_count)

#define AlMsg_set_msg_char_length(the_msg, char_count) \
  (the_msg->msg_char_length = char_count)

#define AlMsg_set_body_line_length(the_msg, line_count) \
  (the_msg->body_line_length = line_count)

#define AlMsg_set_body_char_length(the_msg, char_count) \
  (the_msg->body_char_length = char_count)

#define AlMsg_set_number_of_user_properties(the_msg, num) \
  (the_msg->number_of_user_properties = num)

#define AlMsg_set_state_field_count(the_msg, count) \
  (the_msg->state_field_count = count)

#define AlMsg_set_unprintables_in_body(the_msg, unprintable_chars_flag) \
    (the_msg->unprintables_in_body = unprintable_chars_flag)

/*****************************************************************************/
/***********  MACRO :: Set an Error Code for a Failed Msg Operation  *********/
/*****************************************************************************/

#define AlMsg_set_error_code(the_msg, error_number) \
         ((the_msg->error_code) = error_number)

/*****************************************************************************/
/**************   Get an Error Code for a Failed Msg Operation  **************/
/*****************************************************************************/

       /* This function is publicly accessible */

int AlMsg_get_error_code(the_msg)
     Msg the_msg;
 { /* Make sure Msg exists */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));
   return(the_msg->error_code);
  };                  /* end of AlMsg>_get_error_code()  */

/*****************************************************************************/
/***********   Get an Error-Message for a Failed Msg Operation  **************/
/*****************************************************************************/

       /* This function is publicly accessible */

const char *AlMsg_get_error_msg(the_msg)
     Msg the_msg;
 { /* Make sure Msg exists */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));
   return(AlMsg_error_strings[the_msg->error_code]);
  };                   /* end of AlMsg_get_error_msg()  */

/*****************************************************************************/
/***************   Clear the Error Code in a Message Object  *****************/
/*****************************************************************************/

       /* This function is publicly accessible */

NORET AlMsg_clear_error_code(the_msg)
     Msg the_msg;
 { 
   AlMsg_debug("Msg >> Entering AlMsg_clear_error_code()");

   if (the_msg != NULL)
     the_msg->error_code = 0;
   AlMsg_debug("Msg << Exiting AlMsg_clear_error_code()");

  };                   /* end of AlMsg_clear_error_msg()  */

/*****************************************************************************/
/*****************  Retrieve States of a Message  ****************************/
/*****************************************************************************/

       /* These functions are publicly accessible */

char *AlMsg_name(the_msg)
     Msg the_msg;
 { /* Return the name of this msg */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   if (the_msg->name_of_msg == NULL)
     return(AlMsgNAMEDEFAULT);
   else
     return(the_msg->name_of_msg);
 };

Bool AlMsg_get_removed_flag(the_msg)
     Msg the_msg;
 { /* Return the number of fields this message has */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->removed_flag);
 };

int AlMsg_get_num_of_fields(the_msg)
     Msg the_msg;
 { /* Return the number of fields this message has */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->number_of_fields);
 };

int AlMsg_get_msg_line_length(the_msg)
     Msg the_msg;
 { /* Return the number of new-lines in this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->msg_line_length);
 };

int AlMsg_get_msg_char_length(the_msg)
     Msg the_msg;
 { /* Return the number of characters in this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->msg_char_length);
 };

int AlMsg_get_body_line_length(the_msg)
     Msg the_msg;
 { /* Return the number of new-lines in the body of this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->body_line_length);
 };

int AlMsg_get_body_char_length(the_msg)
     Msg the_msg;
 { /* Return the number of characters in the body of this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->body_char_length);
 };

char *AlMsg_get_msg_body(the_msg)
     Msg the_msg;
 { /* Return the body of this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->text_body);
 };

int AlMsg_get_number_of_user_properties(the_msg)
     Msg the_msg;
 { /* Return the number of user properties assoc. to this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->number_of_user_properties);
 };

int AlMsg_get_num_perm_uprops(the_msg)
     Msg the_msg;
 { /* Return the number of permanent user properties assoc. to this message */
  int i, darray_size, total;
  Darray value_darray;
  MsgUpropField *uprop_field;

   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   total = 0;
   value_darray = Darray_create();
   if (the_msg->user_property_registry != NULL)
     { 
       Registry_fetch_contents(the_msg->user_property_registry,
                               NULL, value_darray);
       darray_size = Darray_len(value_darray);

       /* Loop through all of the User Properties and write each into buffer */
       /* ------------------------------------------------------------------ */
       for (i = 0; i < darray_size; i++)
         { uprop_field = (MsgUpropField *) Darray_reml(value_darray);
	   if (uprop_field->permanent == Bool_TRUE)
             total++;
	 };
     };
   Darray_destroy(value_darray);
   return(total);
 };

int AlMsg_get_state_field_count(the_msg)
     Msg the_msg;
 { /* Return the number of permanent user properties assoc. to this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->state_field_count);
 };

int AlMsg_get_num_rules_fired_state(the_msg)
     Msg the_msg;
 { /* Return the number of rules fired on this message */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->num_rules_fired);
 };

int AlMsg_get_how_many_times_moved(the_msg)
     Msg the_msg;
 { /* Return the number of times this message was moved */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->how_many_times_moved);
 };

char *AlMsg_get_moved_from_state(the_msg)
     Msg the_msg;
 { /* Return the name of the folder from which this message was moved */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->moved_from);
 };

Bool AlMsg_unprintables_in_body(the_msg)
     Msg the_msg;
{ /* Return (a Bool) if the body of the message contained unprintable chars */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->unprintables_in_body);
 };

Bool AlMsg_get_suppress_states_out(the_msg)
     Msg the_msg;
{ /* Return (a Bool) if the states of the msg are not to be written out */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->suppress_states_out);
 };

Bool AlMsg_get_suppress_uprops_out(the_msg)
     Msg the_msg;
{ /* Return (a Bool) if user properties of the msg are not to be written out */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->suppress_uprops_out);
 };

unsigned int AlMsg_get_init_uprop_field_count(the_msg)
     Msg the_msg;
 { /* Return the number of times this message was moved */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->init_uprop_field_count);
 };


/*****************************************************************************/
/****************  Store the States of a Message  ****************************/
/*****************************************************************************/

       /* These functions are publicly accessible */

NORET AlMsg_set_name(the_msg, name)   /* note this function not used        */
     Msg the_msg;                     /* fully. (no-one calls it yet.)      */
     char *name;
 { /* Set the name of this msg */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* First free any old allocated space of name being replaced */
   /* --------------------------------------------------------- */
   if (the_msg->name_of_msg != NULL)
     Memory_free((VOIDP) the_msg->name_of_msg);

   /* Set new name of msg, allocating space if needed */
   /* ----------------------------------------------- */
   if (name == NULL)
     the_msg->name_of_msg = NULL;
   else
     { the_msg->name_of_msg = Memory_allocate(strlen(name) + 1);
       strcpy(the_msg->name_of_msg, name);
     };
 };

NORET AlMsg_set_removed_flag(the_msg, flag)
     Msg the_msg;
     Bool flag;
 { /* Return the number of fields this message has */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   the_msg->removed_flag = flag;
 };

Bool AlMsg_set_num_rules_fired_state(the_msg, count)
     Msg the_msg;
     int count;
 { 
   AlMsg_debug("Msg >> Entering AlMsg_set_num_rules_fired_state()");
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Store the number of rules fired on this message */
   /* ----------------------------------------------- */
   the_msg->num_rules_fired = count;
   AlMsg_debug("Msg << Exiting AlMsg_set_num_rules_fired_state()");
   return(Bool_TRUE);
 };

int AlMsg_incr_num_rules_fired_state(the_msg, increment)
     Msg the_msg;
     int increment;
 { 
   AlMsg_debug("Msg >> Entering AlMsg_incr_num_rules_fired_state()");
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Increment the number of rules fired on this message */
   /* --------------------------------------------------- */
   the_msg->num_rules_fired += increment;
   AlMsg_debug("Msg << Exiting AlMsg_incr_num_rules_fired_state()");
   return(the_msg->num_rules_fired);
 };

Bool AlMsg_set_how_many_times_moved(the_msg, count)
     Msg the_msg;
     int count;
 { 
   AlMsg_debug("Msg >> Entering AlMsg_set_how_many_times_moved()");
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Store the number of times this message was moved */
   the_msg->how_many_times_moved = count;
   AlMsg_debug("Msg << Exiting AlMsg_set_how_many_times_moved()");
   return(Bool_TRUE);
 };

Bool AlMsg_set_moved_from_state(the_msg, folder_name)
     Msg the_msg;
     const char *folder_name;
 { 
   char *f_name;

   AlMsg_debug("Msg >> Entering AlMsg_set_moved_from_state()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Store the name of the folder from which this message was moved */
   if (folder_name == NULL)
     { the_msg->moved_from = NULL;
       AlMsg_debug("Msg << Exiting AlMsg_set_moved_from_state()");
       return(Bool_TRUE);
     }
   else
     { if (the_msg->moved_from != NULL)
	 Memory_free((VOIDP) the_msg->moved_from);
       f_name = (char *) Memory_allocate( (long) strlen(folder_name) + 1);
       strcpy(f_name, folder_name);
       the_msg->moved_from = f_name;
       AlMsg_debug("Msg << Exiting AlMsg_set_moved_from_state()");
       return(Bool_TRUE);
     };
 };

NORET AlMsg_set_suppress_states_out(the_msg, off_flag)
     Msg the_msg;
     Bool off_flag;
{ /* Return (a Bool) if the states of the msg are not to be written out */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   the_msg->suppress_states_out = off_flag;
 };

NORET AlMsg_set_suppress_uprops_out(the_msg, off_flag)
     Msg the_msg;
     Bool off_flag;
{ /* Set (a Bool) if user properties of the msg are not to be written out */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   the_msg->suppress_uprops_out = off_flag;
 };

NORET AlMsg_set_init_uprop_field_count(the_msg, num)
     Msg the_msg;
     unsigned int num;
 { /* Set the number of times this message was moved */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   the_msg->init_uprop_field_count = num;
 };

/*****************************************************************************/
/*********  Store/Retrieve Folder related Info of a Message  *****************/
/*****************************************************************************/

       /* These functions are publicly accessible */

Bool AlMsg_set_folder(the_msg, the_folder)
     Msg the_msg;
     AlFolder the_folder;
 {
   AlMsg_debug("Msg >> Entering AlMsg_set_folder()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_folder != NULL);  /* the_folder exists */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_msg->parent_folder = the_folder;
   AlMsg_debug("Msg << Exiting AlMsg_set_folder()");
   return(Bool_TRUE);
 };

AlFolder AlMsg_get_folder(the_msg)
     Msg the_msg;
 { /* Return the folder object from which this message was created */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   return(the_msg->parent_folder);
 };

NORET AlMsg_set_msg_buffer(the_msg, buff)    /* called by the folder object */
     Msg the_msg;
     char *buff;
 { 
   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL)); /* Msg exists  */
   assert(buff != NULL);                                    /* buff exists */

   /* Set the folder object's buffer from which this message was created */
   /* ------------------------------------------------------------------ */
   the_msg->msg_buffer = buff;
 };

char *AlMsg_get_msg_buffer(the_msg)          /* called by the folder object */
     Msg the_msg;
 { /* Return the folder object's buffer from which this message was created */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));   /* Msg exists */
   return(the_msg->msg_buffer);
 };

Bool AlMsg_set_fs_state_registry(the_msg, the_reg) /* called by folder ob. */
     Msg the_msg;
     Registry the_reg;
 { 
   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL)); /* Msg exists  */
   assert(the_reg != NULL);                                 /* Reg exists  */

   /* Check for a missing Registry pointer */
   /* ------------------------------------ */
   if (the_reg == NULL)
     return(Bool_FALSE);

   /* Set the registry of folder/mailer specific state info  */
   /* ------------------------------------------------------ */
   the_msg->fs_state_registry = the_reg;
   return(Bool_TRUE);
 };

Registry AlMsg_get_fs_state_registry(the_msg) /* called by the folder object */
     Msg the_msg;
 { 
   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL)); /* Msg exists  */

   /* Get the registry of folder/mailer specific state info  */
   /* ------------------------------------------------------ */
   return(the_msg->fs_state_registry);
 };

/*****************************************************************************/
/*******  Retrieve Field Related Information from a Message  *****************/
/*****************************************************************************/

       /* These functions are publicly accessible */

int AlMsg_number_of(the_msg, the_key)
     Msg the_msg;
     char *the_key;
 { MsgField the_fields;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);                              /* the_key exists */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Return the number of fields of key that were present in the message */
   /* ------------------------------------------------------------------- */
   the_fields = (MsgField) AlMsg_Registry_get(the_msg->msg_field_registry, the_key);
   if (the_fields == NULL)
     return(0);                     /* No such field exists */
   else
     return(the_fields->how_many);  /* Return the number of fields */

 };             /* end of AlMsg_number_of()  */

char **AlMsg_get_raw_fields(the_msg, the_key)
     Msg the_msg;
     char *the_key;
 { MsgField the_fields;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);                              /* the_key exists */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Return and array of strings associated to this key, from the msg */
   /* ---------------------------------------------------------------- */
   the_fields = (MsgField) AlMsg_Registry_get(the_msg->msg_field_registry, the_key);
   if (the_fields == NULL)
     return(NULL);                      /* No such field exists */
   else
     return(the_fields->values_array);  /* Return the array of strings */

 };            /* end of AlMsg_get_raw_fields()  */

static Darray AlMsg_install_typed_fields(the_registry, field_type, the_key,
					 the_raw_fields, num_of_fields)
     Registry the_registry;
     AlFieldType field_type;
     char *the_key;
     char *the_raw_fields[];
     int num_of_fields;
 {
   Darray the_darray;
   AlFieldCore field_core;
   int index;
   char *key_copy;

   AlMsg_debug("Msg >> Entering AlMsg_install_typed_fields()");

   if (num_of_fields == 0)
     { AlMsg_debug("Msg << Exiting AlMsg_install_typed_fields()");
       return(NULL);
     };

   key_copy = (char *) Memory_allocate((long) strlen(the_key) + 1);
   strcpy(key_copy, the_key);
   the_darray = Darray_create();
   for(index = 0; index < num_of_fields; index++)
     { field_core =
	 AlFieldCore_create_with_obj(field_type);
       if (AlFieldCore_parse(field_core, the_raw_fields[index]) == Bool_TRUE)
	 { AlFieldCore_set_key(field_core, key_copy);
	   Darray_addh(the_darray, (VOIDP) field_core);
	 }
       else     /* unable to parse this field, ignore it */
	 AlFieldCore_delete(field_core);
     };
   if (Darray_len(the_darray) == 0)
     { /* Unable to convert any raw fields */
       Darray_destroy(the_darray);
       Memory_free((VOIDP) key_copy);
       AlMsg_debug("Msg << Exiting AlMsg_install_typed_fields()");
       return(NULL);
     }
   else
     { AlMsg_Registry_add(the_registry, key_copy, the_darray);
       AlMsg_debug("Msg << Exiting AlMsg_install_typed_fields()");
       return(the_darray);
     };
       AlMsg_debug("Msg << Exiting AlMsg_install_typed_fields()");

 };             /* end of AlMsg_install_typed_fields()  */

static Darray AlMsg_priv_get_fields(the_msg, the_key, field_type)
     Msg the_msg;
     char *the_key;
     AlFieldType field_type;
 {
   Darray the_darray;
   Registry the_registry_of_registries;
   Registry the_registry;

   AlMsg_debug("Msg >> Entering AlMsg_priv_get_fields()");

   /* First, get the Msg ob's Registry of field-core registries.           */
   /* This is a registry of registries, indexed by FieldTypes.             */
   /* Each of these sub-registries contains darrays of FieldCore objects   */
   /* representing the origin msg fields cast into a particular FieldType. */
   /* These sub-registries are indexed by the key string of the msg field. */
   /* -------------------------------------------------------------------- */
   the_registry_of_registries = the_msg->registry_of_FC_registries;
   if (the_registry_of_registries == NULL)
     { /* If the Registry of registries doesn't exist yet, then create it. */
       the_msg->registry_of_FC_registries = AlMsg_ptr_Registry_create();
       the_registry_of_registries = the_msg->registry_of_FC_registries;
     };

   /* Get the sub-registry for this FieldType and the darray of FieldCores. */
   /* --------------------------------------------------------------------- */
   the_registry = (Registry) 
                   AlMsg_Registry_get(the_registry_of_registries, field_type);
   if (the_registry == NULL)
     { /* If the sub-registry doesn't exist yet, then create it now. */
       the_registry = AlMsg_caseless_str_Registry_create();
       AlMsg_Registry_add(the_registry_of_registries, 
			  field_type, the_registry);
       the_darray = NULL;   /* No darray in a new registry, yet. */
     }
   else
     the_darray = (Darray) 
                    AlMsg_Registry_get(the_registry, the_key); /* get darray */

   /* Append the FieldCores requested to the_darray specified.               */
   /* If the data requested hasn't already been converted to FieldCores,     */
   /* Then convert it now, and install the new FieldCores into the_registry. */
   /* ---------------------------------------------------------------------- */
   if (the_darray == NULL)
     { /* Data hasn't yet been converted into FieldCores, do it now. */
       the_darray =
	  AlMsg_install_typed_fields(the_registry, field_type, the_key,
				     AlMsg_get_raw_fields(the_msg, the_key),
				     AlMsg_number_of(the_msg, the_key));
     };
   if (the_darray == NULL)
     /* Unable to convert any raw fields of this key string into       */
     /* FieldCores of this FieldType. (Possibly no match to this key). */
     { AlMsg_debug("Msg << Exiting AlMsg_priv_get_fields()");
       return(NULL);
	}
   else
     { AlMsg_debug("Msg << Exiting AlMsg_priv_get_fields()");
       return(the_darray);
     };

 };                /* end of AlMsg_priv_get_fields()  */

Bool AlMsg_get_fields(the_msg, the_key, field_type, requestors_darray)
     Msg the_msg;
     const char *the_key;
     AlFieldType field_type;
     Darray requestors_darray;
 {
   Darray the_darray;

   AlMsg_debug("Msg >> Entering AlMsg_get_fields()");
   the_darray = AlMsg_priv_get_fields(the_msg, the_key, field_type);
   if (the_darray == NULL)
     { AlMsg_debug("Msg << Exiting AlMsg_get_fields()");
       return(Bool_FALSE);
     }
   else
     {
       AlMsg_append_darray(requestors_darray, the_darray);
       AlMsg_debug("Msg << Exiting AlMsg_get_fields()");
       return(Bool_TRUE);
     };
 };                /* end of AlMsg_get_fields()  */

int AlMsg_number_of_type(the_msg, the_key, field_type)
     Msg the_msg;
     char *the_key;
     AlFieldType field_type;
 { 
   Darray the_darray;

   if ((the_darray = AlMsg_priv_get_fields(the_msg, the_key, field_type))
       != NULL)
     return(Darray_len(the_darray));
   else
     return(0);

 };                 /* end of AlMsg_number_of_type()  */

AlFieldCore AlMsg_get_nth_field(the_msg, the_key, field_type, index)
     Msg the_msg;
     char *the_key;
     AlFieldType field_type;
     unsigned index;
 { 
   unsigned len;
   Darray the_darray;

   if ((the_darray = AlMsg_priv_get_fields(the_msg, the_key, field_type))
       != NULL)
     len = Darray_len(the_darray);
   else
     len = 0;
   if (index < len)
     return((AlFieldCore) Darray_get(the_darray, index));
   else
     return(NULL);
 };                 /* end of AlMsg_get_nth_field()  */

AlFieldCore AlMsg_get_indexed_field(the_msg, the_key, indices, operators,
			     field_type)
     Msg the_msg;
     char *the_key;
     unsigned indices[];
     char operators[];        /* operators are only one char long */
     AlFieldType field_type;
 { int index, last_index;
   AlFieldCore field_core;
   char *results_string, *next_operand, **raw_fields;
   IndexOperatorProc *index_op_proc;

   AlMsg_debug("Msg >> Entering AlMsg_get_indexed_field()");
   raw_fields = AlMsg_get_raw_fields(the_msg, the_key);
   last_index = strlen(operators);
   results_string = raw_fields[indices[0]];
   for (index = 1; index < last_index; index ++)
     { index_op_proc = 
	    AlFieldType_get_index_op_proc(field_type, &(operators[index - 1]));
       next_operand = raw_fields[indices[index]];
       results_string = (index_op_proc)(results_string, next_operand);
     };
   field_core =
         AlFieldCore_create_with_obj(field_type);
   if (AlFieldCore_parse(field_core, results_string) == Bool_TRUE)
     { AlFieldCore_set_key(field_core, the_key);
       AlMsg_debug("Msg << Exiting AlMsg_get_indexed_field()");
       return(field_core);
     }
   else
     { AlMsg_debug("Msg << Exiting AlMsg_get_indexed_field()");
       return(NULL);
     };
 };                    /* end of AlMsg_get_indexed_field()  */

/*****************************************************************************/
/*******  Store/Retrieve User Property related Info of a Message  ************/
/*****************************************************************************/

       /* These functions are publicly accessible */

/* Manipulate User Properties */
/* -------------------------- */

NORET AlMsg_set_uprop(the_msg, the_key, the_value)
     Msg the_msg;
     char *the_key;
     char *the_value;
 { char *key_copy;
   MsgUpropField *the_field;

   AlMsg_debug("Msg >> Entering AlMsg_set_uprop()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);    /* the_key exists   */
   assert(the_value != NULL);  /* the_value exists */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *)
                AlMsg_Registry_get(the_msg->user_property_registry, the_key);

   /* If the user property hasn't been created yet, then create it */
   /* ------------------------------------------------------------ */
   if (the_field == NULL)
     { the_field = (MsgUpropField *) Memory_allocate(sizeof(MsgUpropField));
       the_field->length = strlen(the_value);
       the_field->value = (char *)
	                   Memory_allocate((long) (the_field->length + 1));
       strcpy(the_field->value, the_value);
       the_field->permanent = Bool_FALSE;  /* default is a temporary uprop */
       key_copy = (char *) Memory_allocate((long) (strlen(the_key) + 1));
       strcpy(key_copy, the_key);
       the_field->key = key_copy;
       AlMsg_Registry_add(the_msg->user_property_registry, 
		     key_copy, the_field);
     }
   else
   /* If the user property has been already created, then update it */
   /* ------------------------------------------------------------- */
     { /* Un-reference the old value, first */
       /* --------------------------------- */
       the_msg->uprop_size -= (the_field->length + 1);
       Memory_free((VOIDP) the_field->value);

       /* Create and store a copy of the new value */
       /* ---------------------------------------- */
       the_field->length = strlen(the_value);
       the_field->value = (char *)
	                   Memory_allocate((long) (the_field->length + 1));
       strcpy(the_field->value, the_value);
     };

   the_msg->uprop_size += (the_field->length + 1);
   AlMsg_debug("Msg << Exiting AlMsg_set_uprop()");

 };            /* end of AlMsg_set_uprop()  */

Bool AlMsg_set_uprop_permanence(the_msg, the_key, permanence_flag)
     Msg the_msg;
     char *the_key;
     Bool permanence_flag;
 { 
   MsgUpropField *the_field;

   AlMsg_debug("Msg >> Entering AlMsg_set_uprop_permanence()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);    /* the_key exists   */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *)
                AlMsg_Registry_get(the_msg->user_property_registry, the_key);

   /* If the user property hasn't been created yet, then flag error */
   /* ------------------------------------------------------------- */
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_PERM);
       AlMsg_debug("Msg << Exiting AlMsg_set_uprop_permanence() : Failed");
       return(Bool_FALSE);
     }
   else
       the_field->permanent = permanence_flag;
   AlMsg_debug("Msg << Exiting AlMsg_set_uprop_permanence()");
   return(Bool_TRUE);
 };                    /* end of AlMsg_set_uprop_permanence()  */

Bool AlMsg_delete_uprop(the_msg, the_key)
     Msg the_msg;
     char *the_key;
 { 
   MsgUpropField *the_field;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);    /* the_key exists   */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *) AlMsg_Registry_get(the_msg->user_property_registry,
					       the_key);
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_DELETE);
       return(Bool_FALSE);      /* no action needed, this uprop didn't exist */
     }
   else
     { /* Un-reference the old value, first */
       /* --------------------------------- */
       the_msg->uprop_size -= (the_field->length + 1);
       Memory_free((VOIDP) the_field->value);
       Memory_free((VOIDP) the_field->key);
       AlMsg_Registry_remove(the_msg->user_property_registry, the_key);

       /* Next, destroy the field */
       /* ----------------------- */
       Memory_free((VOIDP) the_field);
       return(Bool_TRUE);
     };
 };               /* end of AlMsg_delete_uprop()  */


Bool AlMsg_append_uprop(the_msg, the_key, tail_value)
     Msg the_msg;
     char *the_key;
     char *tail_value;
 { 
   MsgUpropField *the_field;
   int old_length;
   char *old_value;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);       /* the_key exists      */
   assert(tail_value != NULL);    /* tail_value exists   */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *)
                AlMsg_Registry_get(the_msg->user_property_registry, the_key);
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_APPEND);
       return(Bool_FALSE);      /* Error, this uprop didn't exist */
     }
   else
     { old_length = the_field->length;
       old_value  = the_field->value;

       /* Create and store a copy of the new value */
       /* ---------------------------------------- */
       the_field->length += strlen(tail_value);
       the_field->value = (char *)
	                   Memory_allocate((long) the_field->length + 1);
       /* Copy in old value */
       strcpy(the_field->value, old_value);
       /* Append on the (tail) new value */
       strcpy((the_field->value + old_length), tail_value);
       /* update the message object's uprop state info */
       the_msg->uprop_size += strlen(tail_value);

       /* De-allocate the old value */
       /* ------------------------- */
       Memory_free((VOIDP) old_value);

       return(Bool_TRUE);
     };
 };                 /* end of AlMsg_append_uprop()  */

Bool AlMsg_rename_uprop(the_msg, the_key, new_key)
     Msg the_msg;
     char *the_key;
     char *new_key;
 { 
   MsgUpropField *the_field, *nonexistant_field;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);       /* the_key exists      */
   assert(new_key != NULL);       /* new_key exists      */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *) AlMsg_Registry_get(the_msg->user_property_registry,
					       the_key);
   nonexistant_field = (MsgUpropField *) 
                       AlMsg_Registry_get(the_msg->user_property_registry, new_key);

   /* If the user property hasn't been created yet, then return error */
   /* --------------------------------------------------------------- */
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_RENAME);
       return(Bool_FALSE);      /* no action needed, this uprop didn't exist */
     }
   else if (nonexistant_field != NULL)
   /* If a user property already exists under new uprop name, return error */
   /* -------------------------------------------------------------------- */
     { AlMsg_set_error_code(the_msg, UPROP_EXISTANT_CANT_RENAME);
       return(Bool_FALSE);      /* no action taken */
     }
   else
   /* If the user property has been already created, then rename it */
   /* ------------------------------------------------------------- */
     { /* Un-reference the old key, first */
       /* --------------------------------- */
       AlMsg_Registry_remove(the_msg->user_property_registry, the_key);
       Memory_free((VOIDP) the_field->key);

       /* Create and store a copy of the new value */
       /* ---------------------------------------- */
       the_field->key = (char *) Memory_allocate((long) strlen(new_key) + 1);
       strcpy(the_field->key, new_key);
       AlMsg_Registry_add(the_msg->user_property_registry,
		    the_field->key, the_field);
       return(Bool_TRUE);
     };
 };            /* end of AlMsg_rename_uprop()  */

Bool AlMsg_copy_uprop(the_msg, the_key, new_key)
     Msg the_msg;
     char *the_key;
     char *new_key;
 { 
   MsgUpropField *the_field;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);       /* the_key exists      */
   assert(new_key != NULL);       /* new_key exists      */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *)
               AlMsg_Registry_get(the_msg->user_property_registry, the_key);

   /* If the user property hasn't been created yet, then return error */
   /* --------------------------------------------------------------- */
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_COPY);
       return(Bool_FALSE);      /* Error, this uprop didn't exist */
     }
   else 
   /* If the user property does exist, then copy it into a new property */
   /* ----------------------------------------------------------------- */
     { AlMsg_set_uprop(the_msg, new_key, the_field->value);
       return(Bool_TRUE);
     };
 };            /* end of AlMsg_copy_uprop()  */


/* Get User Property Information */
/* ----------------------------- */

const char *AlMsg_get_uprop(the_msg, the_key)
     Msg the_msg;
     const char *the_key;
 { 
   MsgUpropField *the_field;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);    /* the_key exists   */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *) 
     AlMsg_Registry_get(the_msg->user_property_registry, the_key);
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_GET);
       return(NULL);      /* no action needed, this uprop didn't exist */
     }
   else
     { /* Return the value of this user property */
       /* -------------------------------------- */
       return(the_field->value);
     };
 };               /* end of AlMsg_get_uprop()  */

int AlMsg_get_length_uprop(the_msg, the_key)
     Msg the_msg;
     const char *the_key;
 { 
   MsgUpropField *the_field;

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(the_key != NULL);    /* the_key exists   */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   the_field = (MsgUpropField *) 
     AlMsg_Registry_get(the_msg->user_property_registry, the_key);
   if (the_field == NULL)
     { AlMsg_set_error_code(the_msg, UPROP_NONEXISTANT_CANT_GETLEN);
       return(-1);      /* no action needed, this uprop didn't exist */
     }
   else
     { /* Return the value of this user property */
       /* -------------------------------------- */
       return(the_field->length);
     };
 };               /* end of AlMsg_get_length_uprop()  */

/*****************************************************************************/

NORET AlMsg_ctoh(c, h1p, h2p) 
     char c;            /* a char to convert to a 2 char  hex     */
     char *h1p, *h2p;   /* pts for where to place the 2 hex chars */
 {
   unsigned int h, n;

   n = (unsigned int) c;
   h = n % 16;                            /* Grab low order hex 0<=h<16  */
   *h2p = (h <= 9) ? h+'0' : h+'a'-10;    /* Convert int to hex char     */
   n /= 16;
   h = ((int) n) % 16;                    /* Grab high order hex 0<=h<16 */
   *h1p = (h <= 9) ? h+'0' : h+'a'-10;    /* Convert int to hex char     */
   
 };               /* end of AlMsg_ctoh()  */

/*****************************************************************************/
/****  A routine for encoding a delimeted str field into an output buffer ****/
/*****************************************************************************/

int AlMsg_encode_string(buf_ptr, bp_ptr, bufsz_ptr, str, delimeter, del_len,
                        beginning_offset)
     char **buf_ptr;   /* points to the ptr to the start of the buffer      */
     char **bp_ptr;    /* points to the ptr into the buffer (current index) */
     int  *bufsz_ptr;  /* gives the current size of the allocated buffer    */
     char *str;        /* string to be encoded and placed into the buffer   */
     char *delimeter;  /* separator between fields in buf, will follow str  */
     int  del_len;     /* length of the delimeter string                    */
     int  beginning_offset; /* length of current line so far. (limit to 80) */
 {
   int len, buf_size, offset, i;
   char *buffer, *bp;
   int new_len;               /* current char offset of last line in buffer */

   assert(str != NULL);

   /* Initialize buffer pointers */
   /* -------------------------- */
   buffer   = *buf_ptr;
   bp       = *bp_ptr;
   buf_size = *bufsz_ptr;

   /* Load the string into the buffer, encoding it along the way */
   /* ---------------------------------------------------------- */
   new_len = beginning_offset;
   len = strlen(str);
   for(i = 0; i < len; i++)
     { if ((bp + 15 + del_len) > (buffer + buf_size))
	 { /* Encoding has expanded str too big, need to resize the buffer. */
	   /* ------------------------------------------------------------- */
           offset = bp - buffer;      /* store current offset into buffer */
	   buf_size += (80 + del_len);
           buffer = (char *)
                       Memory_reallocate((VOIDP) buffer, (long) buf_size);
           bp = buffer + offset;      /* set ptr at offset into new buffer */
	 };
       if (new_len > 78)              /* should still fit on one line   */
         { *bp     = '\n';            /* if not, place standard mailer  */
           *(++bp) = ' ';             /* of a "\n " into buffer to flag */
           bp++;                      /* a continuation line,           */
           new_len = 0;               /* and reset the counter.         */
         };
       if ((DELIM_CH == str[i]) OR (ESC_CH == str[i]))
	 { *bp     = ESC_CH;          /* Place an escape infront of the char */
           *(++bp) = str[i];          /* then place the character in buffer. */
           new_len += 2;
	 }
       else if ((AlMsg_unprintable_char(str[i]) == Bool_TRUE)
	         OR (str[i] == '\n'))
	 { *bp     = ESC_CH;           /* Place an escape infront of hex val */
           AlMsg_ctoh(str[i], (bp +1), (bp + 2)); /* convert unprintables in */
                                                 /* string to hex characters */
           bp += 2;
           new_len += 3;
	 }
       else
	 { *bp = str[i];
           new_len++;
	 };
       bp++;
     };

   /* Copy the delimeter into the buffer */
   /* ---------------------------------- */
   for (i = 0; i < del_len; i++)
     *(bp++) = delimeter[i];
   new_len += del_len;

   /* Place the terminating nul at the end of the string */
   /* -------------------------------------------------- */
   *bp = '\0';

   /* Update the necessary pointers. */
   /* ------------------------------ */
   *buf_ptr   = buffer;
   *bp_ptr    = bp;
   *bufsz_ptr = buf_size;

   return(new_len);     /* current char offset of last line in buffer */

 };             /* end of AlMsg_encode_string()  */

/*****************************************************************************/
/***  Generalized routine for encoding a 3 str field into an output buffer ***/
/*****************************************************************************/

char *AlMsg_encode_output_field(xlens_key, the_key, the_value)
     char *xlens_key, *the_key, *the_value;
 {
   int buf_size, del_len, cur_line_offset;
   char *buffer, *bp;
   char *delimeter, separator_delimeter[2];

   /* Create a dynamic buffer at some initially reasonable size */
   /* --------------------------------------------------------- */
   buf_size = strlen(xlens_key) + strlen(the_key) + strlen(the_value) + 21;
   buffer = (char *) Memory_allocate(buf_size);
   bp = buffer;
   delimeter = ENCODING_DELIMETER;
   del_len = strlen(delimeter);
   separator_delimeter[0] = DELIM_CH;
   separator_delimeter[1] = '\0';

   /* Load the first string into the buffer, encoding it along the way */
   /* ---------------------------------------------------------------- */
   cur_line_offset = AlMsg_encode_string(&buffer, &bp, &buf_size, xlens_key,
		                         " ", 1, 0);

   /* Load the second string into the buffer, encoding it along the way */
   /* ----------------------------------------------------------------- */
   cur_line_offset = AlMsg_encode_string(&buffer, &bp, &buf_size, the_key, 
				      separator_delimeter, 1, cur_line_offset);

   /* Load the third string into the buffer, encoding it along the way */
   /* ---------------------------------------------------------------- */
   cur_line_offset = AlMsg_encode_string(&buffer, &bp, &buf_size, the_value,
					 delimeter, del_len, cur_line_offset);

   /* Place the new line and terminating nul at the end of the string */
   /* --------------------------------------------------------------- */
   *(bp++) = '\n';
   *bp = '\0';

   return(buffer);

 };             /* end of AlMsg_encode_output_field()  */

/*****************************************************************************/
/***************  Write User Properties into a buffer. ***********************/
/*****************************************************************************/

char *AlMsg_write_uprops_to_buffer(the_msg)
     Msg the_msg;
 {
  int i, darray_size, temp_length, offset;
  long buffer_size;
  char *buffer, *bp, *temp_buf;
  Darray value_darray;
  MsgUpropField *uprop_field;

  /* Create a buffer. */
  /* ---------------- */
  buffer_size = (long) the_msg->uprop_size
                 + strlen(AlMsg_UPROP_PREFIX)
		 + (the_msg->number_of_user_properties
				       * (4 + strlen(AlMsg_UPROP_PREFIX)))
		 + strlen(FIELD_COUNT_KEY) + 155;
  buffer = (char *) Memory_allocate(buffer_size);
  bp = buffer;
  *bp = '\0';

   /* Print out the User Property Registry's contents to the buffer. */
   /* -------------------------------------------------------------- */
   value_darray = Darray_create();
   if (the_msg->user_property_registry != NULL)
     { 
       Registry_fetch_contents(the_msg->user_property_registry,
                               NULL, value_darray);
       darray_size = Darray_len(value_darray);

       /* Loop through all of the User Properties and write each into buffer */
       /* ------------------------------------------------------------------ */
       for (i = 0; i < darray_size; i++)
         { uprop_field = (MsgUpropField *) Darray_reml(value_darray);
	   if (uprop_field->permanent == Bool_TRUE)
             { 
               temp_buf = AlMsg_encode_output_field(AlMsg_UPROP_PREFIX,
						    uprop_field->key,
						    uprop_field->value);
               temp_length = strlen(temp_buf);
               if ((bp + temp_length) > (buffer + buffer_size))
                 { /* New string won't fit, must first resize buffer */
	           /* ---------------------------------------------- */
                   offset = bp - buffer;    /* store offset from buffer */
                   buffer_size += (temp_length + 155);
                   buffer = (char *)
                               Memory_reallocate((VOIDP) buffer, buffer_size);
                   bp = buffer + offset;  /* get index to pt into new buffer */
                 };
               strcpy(bp, temp_buf); /* Load encoded string into output buf. */
               bp += temp_length;    /* Update buffer's index pointer.       */
               *bp = '\0';
               Memory_free(temp_buf); /* Free temp buffer, its not needed.   */
	     };
	 };
     }
   else
     buffer[0] = '\0';

   Darray_destroy(value_darray);

   return(buffer);
 };               /* end of AlMsg_write_uprops_to_buffer()  */

/*****************************************************************************/
/***************  Write State Information into a buffer. *********************/
/*****************************************************************************/

char *AlMsg_write_states_to_buffer(the_msg)
     Msg the_msg;
 {                   /* Write out the State Information into a buffer. */
  int temp_length, offset;
  long buffer_size;
  char *buffer, *bp, *temp_buf, numstr[25];

  /* Create a buffer. */
  /* ---------------- */
  buffer_size = (long) (strlen(AlMsg_STATE_PREFIX) * 5)
                 + strlen(FIELD_COUNT_KEY)
                 + strlen(RULES_FIRED_KEY)
                 + strlen(MOVED_FROM_KEY)
		 + strlen(TIMES_MOVED_KEY)
                 + strlen(AlMsg_get_moved_from_state(the_msg))
		 + 180;
  buffer = (char *) Memory_allocate(buffer_size);
  bp = buffer;
  *bp = '\0';

   /* Write the field count into the buffer, first */
   /* -------------------------------------------- */
   temp_buf = AlMsg_encode_output_field(AlMsg_STATE_PREFIX, FIELD_COUNT_KEY,
                                        "4");
   temp_length = strlen(temp_buf);
   if ((bp + temp_length) > (buffer + buffer_size))
     { /* New string won't fit, must first resize buffer */
       /* ---------------------------------------------- */
       offset = bp - buffer;    /* store offset from buffer */
       buffer_size += (temp_length + 155);
       buffer = (char *)
                   Memory_reallocate((VOIDP) buffer, buffer_size);
       bp= offset + buffer;    /* move index to point into new buffer */
     };
   strcpy(bp, temp_buf);  /* Load encoded string into output buffer. */
   bp += temp_length;     /* Update buffer's index pointer.          */
   Memory_free(temp_buf); /* Free temp buffer, its no longer needed. */

   /* Write the rules-fired state into the buffer. */
   /* -------------------------------------------- */
   sprintf(numstr, "%d%c", AlMsg_get_num_rules_fired_state(the_msg), '\0');
   temp_buf = AlMsg_encode_output_field(AlMsg_STATE_PREFIX, RULES_FIRED_KEY,
                                        numstr);
   temp_length = strlen(temp_buf);
   if ((bp + temp_length) > (buffer + buffer_size))
     { /* New string won't fit, must first resize buffer */
       /* ---------------------------------------------- */
       offset = bp - buffer;    /* store offset from buffer */
       buffer_size += (temp_length + 155);
       buffer = (char *)
                   Memory_reallocate((VOIDP) buffer, buffer_size);
       bp = offset + buffer;  /* move index to point into new buffer */
     };
   strcpy(bp, temp_buf);  /* Load encoded string into output buffer. */
   bp += temp_length;     /* Update buffer's index pointer.          */
   Memory_free(temp_buf); /* Free temp buffer, its no longer needed. */

   /* Write the moved-from state into the buffer. */
   /* ------------------------------------------- */
   temp_buf = AlMsg_encode_output_field(AlMsg_STATE_PREFIX, MOVED_FROM_KEY,
                                        AlMsg_get_moved_from_state(the_msg));
   temp_length = strlen(temp_buf);
   if ((bp + temp_length) > (buffer + buffer_size))
     { /* New string won't fit, must first resize buffer */
       /* ---------------------------------------------- */
       offset = bp - buffer;    /* store offset from buffer */
       buffer_size += (temp_length + 155);
       buffer = (char *)
                   Memory_reallocate((VOIDP) buffer, buffer_size);
       bp = offset + buffer; /* move index to point into new buffer */
     };
   strcpy(bp, temp_buf);  /* Load encoded string into output buffer. */
   bp += temp_length;     /* Update buffer's index pointer.          */
   Memory_free(temp_buf); /* Free temp buffer, its no longer needed. */

   /* Write the times-moved state into the buffer. */
   /* -------------------------------------------- */
   sprintf(numstr, "%d%c", AlMsg_get_how_many_times_moved(the_msg), '\0');
   temp_buf = AlMsg_encode_output_field(AlMsg_STATE_PREFIX, TIMES_MOVED_KEY,
                                        numstr);
   temp_length = strlen(temp_buf);
   if ((bp + temp_length) > (buffer + buffer_size))
     { /* New string won't fit, must first resize buffer */
       /* ---------------------------------------------- */
       offset = bp - buffer;    /* store offset from buffer */
       buffer_size += (temp_length + 155);
       buffer = (char *)
                   Memory_reallocate((VOIDP) buffer, buffer_size);
       bp = offset +  buffer;    /* move index to point into new buffer */
     };
   strcpy(bp, temp_buf);  /* Load encoded string into output buffer. */
   bp += temp_length;     /* Update buffer's index pointer.          */
   Memory_free(temp_buf); /* Free temp buffer, its no longer needed. */
   *bp = '\0';

   return(buffer);
 };               /* end of AlMsg_write_states_to_buffer()  */

/*****************************************************************************/
/***********  Copy a Key String from the Message Buffer to memory  ***********/
/*****************************************************************************/

       /* This function usually called from AlMsg_readkey() */

static NORET AlMsg_copy_string(key_string, start_index, end_index)
   char *key_string;      /* pointer to string in which to place key string  */
   char *start_index;     /* index into buffer at which to start seeking key */
   char *end_index;       /* index into buffer at which key string ends      */
 {
   char *buf_index;       /* index into buffer at current point being viewed */
   char *key_index;       /* index into key string at current point accessed */

   AlMsg_debug("Msg >> Entering AlMsg_copy_string()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(key_string != NULL);       /* key_string exists      */
   assert(start_index != NULL);      /* start_index exists     */
   assert(end_index != NULL);        /* end_index exists       */

   key_index = key_string;
   if ((end_index + 1) != start_index)  /* i.e. if there's something to copy */
     {
       /* Copy the key string from buffer into a key string */
       /* ------------------------------------------------- */
       for (buf_index = start_index; buf_index != end_index; buf_index++)
         { *key_index = *buf_index;
           key_index++;
         };
       *key_index = *end_index;
       key_index++;
     };

   /* Place the terminator on the new key string */
   /* ------------------------------------------- */
   *key_index   = '\0';          /* terminate the string */

   AlMsg_debug("Msg << Exiting AlMsg_copy_string()");

 };                   /* end of AlMsg_copy_string()  */

/*****************************************************************************/

NORET AlMsg_htoc(h1, h2, cp)
     char h1, h2;             /* hex characters to be converted into a char. */
     char *cp;                /* char ptr, where to put result of conversion */
 {
    int c;

    /* Decode the hex digit h1,h2 into its corresponding ascii character. */
    /* ------------------------------------------------------------------ */
    c = 0;
    if ((h1 >= '0') && (h1 <= '9'))
      c = h1 - '0';
    else if ((h1 >= 'a') && (h1 <= 'f'))
      c = h1 - 'a' + 10;
    else if ((h1 >= 'A') && (h1 <= 'F'))
      c = h1 - 'A' + 10;
    else     /* error, not a hex char */
      c = 0;
    c = c << 4 ;                /* move high order hex nibble into place, */
                                /* then add in the lower order hex nibble */
    if ((h2 >= '0') && (h2 <= '9'))
      c += h2 - '0';
    else if ((h2 >= 'a') && (h2 <= 'f'))
      c += h2 - 'a' + 10;
    else if ((h2 >= 'A') && (h2 <= 'F'))
      c += h2 - 'A' + 10;
    else     /* error, not a hex char, flag as a hex 01 (not a null) */
      c = 1;

    /* Place the resulting ascii character into char pt'ed to by cp. */
    /* ------------------------------------------------------------- */
    if (c == 0)
      *cp = (char) 1;     /* nulls aren't allowed */
    else
      *cp = (char) c;

  };                  /* end of AlMsg_htoc()  */

/*****************************************************************************/
/*****  Decode and  Copy a Key String from the Message Buffer to memory  *****/
/*****************************************************************************/

       /* This function usually called from AlMsg_readkey() */

static NORET AlMsg_decode_and_copy_string(key_string, start_index, end_index)
   char *key_string;      /* pointer to string in which to place key string  */
   char *start_index;     /* index into buffer at which to start seeking key */
   char *end_index;       /* index into buffer at which key string ends      */
 {
   char *buf_index;       /* index into buffer at current point being viewed */
   char *key_index;       /* index into key string at current point accessed */
   char delimeter[1];

   AlMsg_debug("Msg >> Entering AlMsg_decode_and_copy_string()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(key_string != NULL);       /* key_string exists      */
   assert(start_index != NULL);      /* start_index exists     */
   assert(end_index != NULL);        /* end_index exists       */

   delimeter[0] = ESC_CH;
   key_index = key_string;
   if ((end_index + 1) > start_index)  /* i.e. if there's something to copy */
     {
       /* Copy the key string from buffer into a key string */
       /* ------------------------------------------------- */
       for (buf_index = start_index; buf_index < end_index; buf_index++)
         { if (*buf_index == '\n')    /* skip over new-line and one space */
             { buf_index++;
               if (*buf_index == ' ')     /* This shouldn't happen.          */
		 *key_index = *buf_index; /* If it does ignore error& use it */
               if (buf_index == end_index)
                 buf_index--;  /* avoid missing the conditional exit */
	     }
           else if (*buf_index != ESC_CH)  /* just copy normal character */
             { *key_index = *buf_index;
               key_index++;
	     }     /* everything after this has an escape character present */
	   else if (*(buf_index + 1) == DELIM_CH)   /* escaped DELIM_CH */
	     { buf_index++;
               *key_index = *buf_index;
               key_index++;
	       if (buf_index == end_index)
                 buf_index--;  /* avoid missing the conditional exit */
	     }
	   else if (*(buf_index + 1) != ESC_CH)   /* escaped hex value */
	     { if ((buf_index + 2) <= end_index)
		 { AlMsg_htoc(*(buf_index + 1), *(buf_index + 2), key_index);
                   key_index++;
                   buf_index += 2;
		 }
               else
                  /* end comes before hex, so just skip to end */
                 buf_index = (end_index - 1);
	     }
	   else                         /* escaped escape-char, use char */
	     { buf_index++;
               *key_index = *buf_index;
               key_index++;
	       if (buf_index == end_index)
                 buf_index--;  /* avoid missing the conditional exit */
	     };
         };
       *key_index = *end_index;
       key_index++;
     };

   /* Place the terminator on the new key string */
   /* ------------------------------------------- */
   *key_index   = '\0';          /* terminate the string */

   AlMsg_debug("Msg << Exiting AlMsg_decode_and_copy_string()");

 };                   /* end of AlMsg_decode_and_copy_string()  */

/*****************************************************************************/
/**************  Read a Key String from the Message Buffer *******************/
/*****************************************************************************/

       /* This function usually called from AlMsg_readfield()  */

char *AlMsg_readkey(buffer_index, line_count, char_count)
    char **buffer_index;  /* Index into buffer at which to start seeking key */
                          /* ptr char* to set new buffer_index to on return  */
     int *line_count;     /* how many new-lines in the message so far        */
     int *char_count;     /* how many characters in the message so far       */
 { 
   char *start_index;     /* index into buffer at which to start seeking key */
   char *end_index;       /* index into buffer at which key string ends      */
   char *key_string;      /* pointer to new string containing the key string */
   int old_char_count;    /* back-up for char_count, incase reset is needed  */
   int old_line_count;    /* back-up for line_count, incase reset is needed  */
   int indentation;       /* number of whitespace characters before key      */

   AlMsg_debug("Msg >> Entering AlMsg_readkey()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(buffer_index != NULL);       /* *buffer_index exists  */
   assert(*buffer_index != NULL);      /* buffer_index exists   */
   assert(line_count != NULL);         /* line_count exists     */
   assert(char_count != NULL);         /* char_count exists     */

   /* Skip over Blank Spaces, Tabs, and New-lines that occur before a key */
   /* ------------------------------------------------------------------- */
   old_char_count = *char_count;
   old_line_count = *line_count;
   indentation = 0;
   for (start_index = *buffer_index;
	((*start_index == ' ') OR (*start_index == '\n')
	 OR (*start_index == '\t'));
	start_index++)
     { if (*start_index == '\n')
	 { (*line_count) ++;
	   indentation = 0;
	 }
       else
	 indentation++;
       (*char_count)++;
     };

   /* Search buffer for start and end of the key string */
   /* ------------------------------------------------- */
   for (end_index = start_index; 
	(*end_index != ':') && (*end_index != '\0') 
	&& (*end_index != '\n') && (*end_index != ' ');
	end_index++)
     { 
       (*char_count)++;
       /* If this line contains no key, a new-line will occur before a ':' */
       /* ---------------------------------------------------------------- */
     };

   /* Copy key into a new string, and return the pointer to it */
   /* -------------------------------------------------------- */
   if ((*end_index == ':') && (indentation == 0))
     {
       key_string = (char *) Memory_allocate(2 + (long)
					      (end_index - start_index));
       AlMsg_copy_string(key_string, start_index, end_index);
       if(*(*buffer_index = (end_index + 1)) == ' ')
         *buffer_index += 1;           /* skip first space after colon */
     }
   else
     { /* Flag end of Msg, all keys are done */
       key_string = NULL;
       *char_count = old_char_count;
       *line_count = old_line_count;
     };

   AlMsg_debug("Msg << Exiting AlMsg_readkey()");
   return(key_string);

 };           /* end of AlMsg_readkey()  */

/*****************************************************************************/
/*************  Read a Value String from the Message Buffer ******************/
/*****************************************************************************/

       /* This function usually called from AlMsg_readfield()  */

char *AlMsg_readvalue(buffer_index, line_count, char_count)
    char **buffer_index;  /* Index into buffer at which to start seeking key */
                          /* ptr char* to set new buffer_index to on return  */
     int *line_count;     /* how many new-lines in the message so far        */
     int *char_count;     /* how many characters in the message so far       */
 { 
   char *start_index;     /* index into buffer at which key string begins    */
   char *end_index;       /* index into buffer at which key string ends      */
   char *value_string;    /* pointer to new string containing the value str  */
   int done_flag;

   AlMsg_debug("Msg >> Entering AlMsg_readvalue()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(buffer_index != NULL);       /* *buffer_index exists  */
   assert(*buffer_index != NULL);      /* buffer_index exists   */
   assert(line_count != NULL);         /* line_count exists     */
   assert(char_count != NULL);         /* char_count exists     */

   done_flag = 0;
   start_index = *buffer_index;

   /* Search buffer for end of the value string */
   /* ----------------------------------------- */
   for (end_index = *buffer_index; 
	((done_flag == 0) && (*end_index != '\0'));
	end_index++)
     { (*char_count) ++;
       /* Value str is terminated by '\n' that is NOT followed by SP or '\t' */
       /* ------------------------------------------------------------------ */
       if (*end_index == '\n')
	 (*line_count) ++;
       if ((*end_index == '\n') && (*(end_index + 1) != ' ')
           && (*(end_index + 1) != '\t'))
	 { done_flag = 1;
           value_string = (char *)
                         Memory_allocate(2 + (long) (end_index - start_index));
           AlMsg_copy_string(value_string, start_index, (end_index - 1));
	 };
     };

   /* Copy value into a new string, and return the pointer to it */
   /* ---------------------------------------------------------- */
   if (done_flag == 0)
     { value_string = (char *)
                         Memory_allocate((long) (end_index - start_index) + 1);
       /* don't include the terminator on the value */
       AlMsg_copy_string(value_string, start_index, (end_index - 1));
     };

   /* Update the buffer index ptr */
   /* --------------------------- */
   *buffer_index = end_index;

   AlMsg_debug("Msg << Exiting AlMsg_readvalue()");
   return(value_string);

 };           /* end of AlMsg_readvalue()  */

/*****************************************************************************/
/*************  Read a Field (Key/Value) from the Message Buffer *************/
/*****************************************************************************/

       /* This function usually called from AlMsg_read_msg()  */

static Bool AlMsg_readfield(buffer_index, key, value, line_count, char_count)
    char **buffer_index;  /* index into buffer at which to start seeking key */
                          /* ptr char* to set new buffer_index to on return  */
     char **key, **value; /* p to Key and Value strings for field being read */
     int *line_count;     /* how many new-lines in the message so far        */
     int *char_count;     /* how many characters in the message so far       */
 { int old_line_count, old_char_count;
   char *old_buffer;

   AlMsg_debug("Msg >> Entering AlMsg_readfield()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(buffer_index != NULL);       /* *buffer_index exists  */
   assert(*buffer_index != NULL);      /* buffer_index exists   */
   assert(key != NULL);                /* *key exists           */
   assert(value != NULL);              /* *value exists         */
   assert(line_count != NULL);         /* line_count exists     */
   assert(char_count != NULL);         /* char_count exists     */

   /* Store away old values, for later restore, if no more fields exist */
   /* ----------------------------------------------------------------- */
   old_buffer = *buffer_index;
   old_line_count = *line_count;
   old_char_count = *char_count;

   *key = AlMsg_readkey(buffer_index, line_count, char_count);
   if (*key == NULL)    /* No key fields to read. Finished reading */
     { *line_count = old_line_count;
       *char_count = old_char_count;
       *buffer_index = old_buffer;
       AlMsg_debug("Msg << Exiting AlMsg_readfield()");
       return(Bool_FALSE);
     }
   else
     {
       /* Found a new field, set its key/value pair */
       /* ----------------------------------------- */
       *value = AlMsg_readvalue(buffer_index, line_count, char_count);
     };

   AlMsg_debug("Msg << Exiting AlMsg_readfield()");
   return(Bool_TRUE);

 };           /* end of AlMsg_readfield()  */

/*****************************************************************************/
/*************  Read the Body (Text) from the Message Buffer  ****************/
/*****************************************************************************/

       /* This function usually called from AlMsg_read_msg()  */

static NORET AlMsg_read_msg_body(the_msg, buffer_index, line_count, char_count)
     Msg the_msg;         /* The Message Object to be associated to the body */
     char **buffer_index; /* Index into buffer at which to start seeking key */
                          /* ptr char* to set new buffer_index to on return  */
     int *line_count;     /* how many new-lines in the message so far        */
     int *char_count;     /* how many characters in the message so far       */
 { 
   int old_line_count, old_char_count;
   char *index;
   char *body;
   Bool unprintable_chars_flag;

   AlMsg_debug("Msg >> Entering AlMsg_read_msg_body()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(buffer_index != NULL);       /* *buffer_index exists  */
   assert(*buffer_index != NULL);      /* buffer_index exists   */
   assert(line_count != NULL);         /* line_count exists     */
   assert(char_count != NULL);         /* char_count exists     */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Store the length info for the header portion of the message */
   /* ----------------------------------------------------------- */
   old_line_count = *line_count;
   old_char_count = *char_count;

   /* Update line count and char count state information */
   /* -------------------------------------------------- */
   unprintable_chars_flag = Bool_FALSE;
   for (index = *buffer_index;
	*index != '\0';
	index++)
     { (*char_count) ++;
       if (*index == '\n')
	 (*line_count) ++;
       if (AlMsg_unprintable_char(*index) == Bool_TRUE)
	 unprintable_chars_flag = Bool_TRUE;
     };

   /* Create the body string */
   /* ---------------------- */
   body = (char *) Memory_allocate((long) (index - *buffer_index) + 1);
   AlMsg_copy_string(body, *buffer_index, (index - 1));

   /* Store the body of the text in the message object, set State Info */
   /* ---------------------------------------------------------------- */
   the_msg->text_body = body;
   AlMsg_set_body_line_length(the_msg, (*line_count - old_line_count));
   AlMsg_set_body_char_length(the_msg, (*char_count - old_char_count));
   AlMsg_set_unprintables_in_body(the_msg, unprintable_chars_flag);

   AlMsg_debug("Msg << Exiting AlMsg_read_msg_body()");

 };               /* end of AlMsg_read_msg_body()  */

/*****************************************************************************/
/*************  Read All Fields in from a Message ****************************/
/*****************************************************************************/

       /* This function usually called from AlMsg_create()  */

static NORET AlMsg_read_msg(the_msg, msg_buffer)
     Msg the_msg;
     char *msg_buffer;
 { 
   char **buffer_index, *key, *value, **new_mem, *buff;
   int field_count, char_count, line_count;
   MsgField the_field;
   Bool field;

   AlMsg_debug("Msg >> Entering AlMsg_read_msg()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(msg_buffer != NULL);         /* msg_buffer exists   */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   field_count = 0;
   char_count  = 0;
   line_count  = 0;
   buff = msg_buffer;
   buffer_index = &buff;
   field = AlMsg_readfield(buffer_index, 
			  &key, &value, &line_count, &char_count);
   while(field == Bool_TRUE)
     { /* Note AlMsg_readfield() allocated memory for the key & value  */
       /* strings, but the key is de-allocated here, and the value     */
       /* will be de-allocated later with the field by AlMsg_destroy() */

       if ((the_field = (MsgField)
	                AlMsg_Registry_get(the_msg->msg_field_registry,
					   key))
	   != NULL)
	 { /* Key is already registered, add a new value to its array */
	   new_mem = (char **)
	              Memory_reallocate((VOIDP) the_field->values_array, 
				       ((the_field->how_many + 1) *
					sizeof(STRING)));
	   new_mem[the_field->how_many] = value;
	   the_field->values_array =  new_mem;
	   (the_field->how_many) ++;
	   AlMsg_Registry_replace_value(the_msg->msg_field_registry, 
			      key, the_field);
           Memory_free((VOIDP) key);   /* don't need this copy of the key */
	 }
       else
	 { /* Key needs to be registered and a value array created */
           the_field = (MsgField) Memory_allocate(sizeof(AlMsgFieldObject));
	   new_mem = (char **) Memory_allocate(sizeof(STRING));
	   new_mem[0] = value;
	   the_field->values_array =  new_mem;
	   the_field->how_many = 1;
	   AlMsg_Registry_add(the_msg->msg_field_registry, key, the_field);
	 };
       field_count++;
       field = AlMsg_readfield(buffer_index, 
			  &key, &value, &line_count, &char_count);
     };

   /* Read the body of the message from the buffer */
   /* -------------------------------------------- */
   AlMsg_read_msg_body(the_msg, buffer_index, &line_count, &char_count);

   /* Set the State Information about the message */
   /* ------------------------------------------- */
   AlMsg_set_num_of_fields(the_msg, field_count);
   AlMsg_set_msg_line_length(the_msg, line_count);
   AlMsg_set_msg_char_length(the_msg, char_count);

   AlMsg_debug("Msg << Exiting AlMsg_read_msg()");

   };             /* end of AlMsg_read_msg()  */

/*****************************************************************************/
/*************  Read an All-purpose Field  ***********************************/
/*****************************************************************************/

       /* This function usually called from AlMsg_read_state()  */
       /* and AlMsg_read_user_properties()                      */

static Bool AlMsg_readafield(buffer_index,
			     key, value, requested_key, error_flag)
     char **buffer_index, **key, **value, *requested_key;
     Bool *error_flag;
 {
   char *end_index, *next_buffer_index;
   char *result, *delimeter, *encoded_buffer;
   int del_len;
   int line_count = 0, char_count = 0;

   AlMsg_debug("Msg >> Entering AlMsg_readafield()");

   /* Sidenote:  This function is flat coded (to run faster). */

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(buffer_index != NULL);         /* *buffer_index exists  */
   assert(*buffer_index != NULL);        /* buffer_index exists   */
   assert(key != NULL);                  /* *key exists           */
   assert(value != NULL);                /* *value exists         */
   assert(requested_key != NULL);        /* requested_key exists  */
   assert(error_flag != NULL);           /* error_flag exists     */

   *error_flag = Bool_FALSE;
   next_buffer_index = *buffer_index;

   /* Check for an empty buffer, first. If empty, then all finished. */
   /* -------------------------------------------------------------- */
   if (**buffer_index == '\0')
     { AlMsg_debug("Msg << Exiting AlMsg_readafield() : END OF STRING (ok)");
       return(Bool_FALSE);       /* Reached End-Of-File (end of string) */
     };

   /* Make sure that the next field is the type of field requested. */
   /* ------------------------------------------------------------- */
   *key = AlMsg_readkey(&next_buffer_index, &line_count, &char_count);
   if (*key == NULL)    /* No key fields to read. Finished reading */
     { AlMsg_debug(
           "Msg << Exiting AlMsg_readafield() : extra, then END OF STRING (ok)"
           );
       return(Bool_FALSE);
     }
   else if (strcmp(*key, requested_key) != 0)   /* didn't match */
     { Memory_free((VOIDP) *key);
       *key = NULL;
       AlMsg_debug(
           "Msg << Exiting AlMsg_readafield() : not the requested key (ok)"
           );
       return(Bool_FALSE);
     }
   else
     { 
       /* Found a field of the correct type. Get its value string. */
       /* -------------------------------------------------------- */
       Memory_free((VOIDP) *key);  /* discard the outer key */
       if (*next_buffer_index == '\0')  /* check for empty buffer */
          { AlMsg_debug(
               "Msg << Exiting AlMsg_readafield() : key had empty value (ok)"
               );
            return(Bool_FALSE);
          };
       encoded_buffer = AlMsg_readvalue(&next_buffer_index,
					 &line_count, &char_count);
     };

   /*  Locate delimeter. (Marks end of the encoded key string.) */
   /* --------------------------------------------------------- */
   for(result = encoded_buffer;
       ((*result != '\0')
        && !((*result == DELIM_CH) && (*(result - 1) != ESC_CH)));
       result++);

   /* Check for an error. (Premature end of string.) */
   /* ---------------------------------------------- */
   if (*result == '\0')         /* File has bad data, flag error & return  */
     { *error_flag = Bool_TRUE;
       *key = NULL;
       *value = NULL;
       Memory_free((VOIDP) encoded_buffer);  /* discard the encoded buffer */
       AlMsg_debug(
         "Msg << Exiting AlMsg_readafield() : GENERALIZED-KEY STRING MISSING");
       return(Bool_FALSE);
     }
   else
     end_index = result;

   /* Allocate the memory for the key string */
   /* -------------------------------------- */
   *key = (char *) Memory_allocate( (long) (end_index - encoded_buffer) + 1);
   AlMsg_decode_and_copy_string(*key, encoded_buffer, (end_index - 1));

   /* Allocate the memory for the value string */
   /* ---------------------------------------- */
   end_index++;
   *value = (char *) Memory_allocate( (long) (strlen(end_index) + 1));
   AlMsg_decode_and_copy_string(*value, end_index,
                                (end_index + strlen(end_index)));

   /* Set buffer index to point at next field, if there is one. */
   /* --------------------------------------------------------- */
   Memory_free((VOIDP) encoded_buffer);      /* discard the encoded buffer */
   *buffer_index = next_buffer_index;
   if (**buffer_index == '\n') /* should be a '\0' or '\n' at this address */
                               /* if its a '\n', it should be skipped over */
     *buffer_index += 1;

   AlMsg_debug("Msg << Exiting AlMsg_readafield()");
   return(Bool_TRUE);

 };              /* end of AlMsg_readafield()  */

/*****************************************************************************/
/**************  Set State Information in from a Buffer  *********************/
/*****************************************************************************/

       /* This function usually called from AlMsg_create()  */

static Bool AlMsg_set_state(the_msg, key, value)
     Msg the_msg;
     char *key, *value;
 {
   AlMsg_debug("Msg >> Entering AlMsg_set_state()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(key != NULL);                  /* key exists           */
   assert(value != NULL);                /* value exists         */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   if (strcmp(key, FIELD_COUNT_KEY) == 0)          /* set field count        */
     AlMsg_set_state_field_count(the_msg, atoi(value));
   else if (strcmp(key, RULES_FIRED_KEY) == 0)     /* set num of rules fired */
     AlMsg_set_num_rules_fired_state(the_msg, atoi(value));
   else if (strcmp(key, MOVED_FROM_KEY) == 0)      /* set moved from folder  */
     AlMsg_set_moved_from_state(the_msg, value);
   else if (strcmp(key, TIMES_MOVED_KEY) == 0)     /* set n-many times moved */
     AlMsg_set_how_many_times_moved(the_msg, atoi(value));
   else
     { AlMsg_debug2("   In AlMsg_set_state(): bad state key =", key);
       AlMsg_debug2("   In AlMsg_set_state(): bad key's val =", value);
       Memory_free((VOIDP) key);
       Memory_free((VOIDP) value);
       AlMsg_debug("Msg << Exiting AlMsg_set_state(): bad state");
       return(Bool_FALSE);                  /* flag and invalid state update */
     };

   Memory_free((VOIDP) key);
   Memory_free((VOIDP) value);
   AlMsg_debug("Msg << Exiting AlMsg_set_state()");
   return(Bool_TRUE);                       /* flag successful state update  */

 };               /* end of AlMsg_set_state()  */

/*****************************************************************************/
/*************  Read State Information in from a Buffer  *********************/
/*****************************************************************************/

       /* This function usually called from AlMsg_create()  */

static Bool AlMsg_read_state(the_msg, state_buffer)
     Msg the_msg;
     char *state_buffer;
 {
   char **buffer_index, *key, *value, *buff;
   Bool field, error_flag;
   int field_count, correct_field_count;

   AlMsg_debug("Msg >> Entering AlMsg_read_state()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(state_buffer != NULL);            /* state_buffer exists         */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   field_count = 0;
   buff = state_buffer;
   buffer_index = &buff;
   field = AlMsg_readafield(buffer_index,
                            &key, &value, AlMsg_STATE_PREFIX, &error_flag);
   if (error_flag == Bool_TRUE)          /* Error occured */
     { AlMsg_set_error_code(the_msg, BAD_STATE_FILE);
       AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);
       Al_warning2(
        "Warning: Bad state info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_STATE_PREFIX, AlMsg_name(the_msg));
       AlMsg_debug("Msg << Exiting AlMsg_read_state()");
       return(Bool_FALSE);
     };
   while (field == Bool_TRUE)
     { if (Bool_TRUE ==   /* Mem is de-alloc'ed here (in set_state), if nec. */
	   AlMsg_set_state(the_msg, key, value)) 
	 field_count ++;
       else
	 { AlMsg_set_error_code(the_msg, BAD_STATE_FILE);
           AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);
           Al_warning2(
                "Warning: Bad state info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_STATE_PREFIX, AlMsg_name(the_msg));
           AlMsg_debug("Msg << Exiting AlMsg_read_state(): set state failed");
	   return(Bool_FALSE);
	 };
       field = AlMsg_readafield(buffer_index, 
                                &key, &value, AlMsg_STATE_PREFIX, &error_flag);
       if (error_flag == Bool_TRUE)         /* Error occured */
	 { AlMsg_set_error_code(the_msg, BAD_STATE_FILE);
           AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);
           Al_warning2(
                "Warning: Bad state info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_STATE_PREFIX, AlMsg_name(the_msg));
           AlMsg_debug("Msg << Exiting AlMsg_read_state(): readafield failed");
	   return(Bool_FALSE);
	 };
     };
   AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);

   correct_field_count = AlMsg_get_state_field_count(the_msg);
   if (field_count < correct_field_count)
     { AlMsg_set_error_code(the_msg, TOO_FEW_STATE_FIELDS);
       Al_warning2(
                "Warning: Bad state info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_STATE_PREFIX, AlMsg_name(the_msg));
       AlMsg_debug("Msg << Exiting AlMsg_read_state()");
       return(Bool_FALSE);
     }
   else if (field_count > correct_field_count)
     { AlMsg_set_error_code(the_msg, TOO_MANY_STATE_FIELDS);
       Al_warning2(
                "Warning: Bad state info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_STATE_PREFIX, AlMsg_name(the_msg));
       AlMsg_debug("Msg << Exiting AlMsg_read_state()");
       return(Bool_FALSE);
     }
   else
     { AlMsg_debug("Msg << Exiting AlMsg_read_state()");
        return(Bool_TRUE);
     };
 };                /* end of AlMsg_read_state()  */

/*****************************************************************************/
/**********  Read User Property Information in from a Buffer  ****************/
/*****************************************************************************/

       /* This function usually called from AlMsg_create()  */

static Bool AlMsg_read_user_properties(the_msg, uprop_buffer)
     Msg the_msg;
     char *uprop_buffer;
 {
   char **buffer_index, *key, *value, *buff;
   Bool field, error_flag;
   int field_count;

   AlMsg_debug("Msg >> Entering AlMsg_read_user_properties()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(uprop_buffer != NULL);            /* uprop_buffer exists         */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   field_count = 0;
   buff = uprop_buffer;
   buffer_index =  &buff;

   /* Check for an empty user property buffer, which is valid */
   /* ------------------------------------------------------- */
   if (*uprop_buffer == '\0')
     { AlMsg_set_number_of_user_properties(the_msg, 0);
       AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);
       AlMsg_debug("Msg << Exiting AlMsg_read_user_properties()");
       return(Bool_TRUE);
     };

   /* Loop through and set up all permanent user properties.  */
   /* Permanent means that they are associated to the message */
   /* and stored for further use; they don't disappear at the */
   /* end of a run.                                           */
   /* ------------------------------------------------------- */

   field = AlMsg_readafield(buffer_index, 
                            &key, &value, AlMsg_UPROP_PREFIX, &error_flag);
   if (error_flag == Bool_TRUE)          /* Error occured */
     { AlMsg_set_error_code(the_msg, BAD_UPROP_FILE);
       AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);
       Al_warning2(
        "Warning: Bad user property info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_UPROP_PREFIX, AlMsg_name(the_msg));
       AlMsg_debug(
        "Msg << Exiting AlMsg_read_user_properties() : ERROR READING A FIELD");
       return(Bool_FALSE);
     };
   while (field == Bool_TRUE)
     { AlMsg_set_uprop(the_msg, key, value);
       AlMsg_set_uprop_permanence(the_msg, key, Bool_TRUE);
       field_count ++;
       field = AlMsg_readafield(buffer_index, 
                                &key, &value, AlMsg_UPROP_PREFIX, &error_flag);
       if (error_flag == Bool_TRUE)          /* Error occured */
	 { AlMsg_set_error_code(the_msg, BAD_UPROP_FILE);
           AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);
           AlMsg_debug(
           "Msg << Exiting AlMsg_read_user_properties() : ERROR READING AFIELD"
           );
           Al_warning2(
        "Warning: Bad user property info in (%s) msg field. Skipping msg:%s\n",
		  AlMsg_UPROP_PREFIX, AlMsg_name(the_msg));
           return(Bool_FALSE); /* skip over this msg, it has bad data */
	 };
     };

   /* Set the number of user properties found in the Uprop file. */
   /* ---------------------------------------------------------- */
   AlMsg_set_number_of_user_properties(the_msg, field_count);
   AlMsg_set_unprintables_in_body(the_msg, Bool_FALSE);     /* if set, unset */
   AlMsg_debug("Msg << Exiting AlMsg_read_user_properties()");
   return(Bool_TRUE);

 };                /* end of AlMsg_read_user_properties()  */

/*****************************************************************************/
/*********  Create a Message Object from three Character Buffers *************/
/*****************************************************************************/

Msg AlMsg_create(msg_buffer, lens_state_buffer,
		 user_prop_buffer, fs_state_registry,
		 folder_obj)
    char *msg_buffer;           /* buffer containing original, raw message   */
    char *lens_state_buffer;    /* buffer containing state information       */
    char *user_prop_buffer;     /* buffer containing user property info      */
    Registry fs_state_registry; /* folder/mailer specific state registry     */
    AlFolder folder_obj;        /* folder object from which this Msg came    */
 {
   Msg the_msg;

   AlMsg_debug("Msg >> Entering AlMsg_create()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(msg_buffer != NULL);            /* msg_buffer exists         */
   assert(lens_state_buffer != NULL);     /* lens_state_buffer exists  */
   assert(user_prop_buffer != NULL);      /* user_prop_buffer exists   */
   assert(fs_state_registry != NULL);     /* fs_state_registry exists  */
   assert(folder_obj != NULL);            /* folder_obj exists         */
          /* create reg to trap bad Msg ptrs */
   assert(((AlMsg_Registry == NULL) ? (AlMsg_Registry = AlMsg_ptr_Registry_create()) : AlMsg_Registry) != NULL);

#if (MSGDEBUG == 1)    /* More DEBUG stuff */
   printf("****************** Buffers passed to AlMsg_create() ***********\n");
   printf("  strlen state = %d,  strlen uprop = %d,  strlen msg buf = %d\n",
          strlen(lens_state_buffer), strlen(user_prop_buffer), 
          strlen(msg_buffer));
   printf("STATE BUFFER follows:\n%s\n", lens_state_buffer);
   printf("UPROP BUFFER follows:\n%s\n", user_prop_buffer);
   printf("MSG BUFFER follows:\n%s\n", msg_buffer);
   printf("************ End of Buffers passed to AlMsg_create() **********\n");
#endif

   /* First check for an error condition */
   /* ---------------------------------- */
   if ((msg_buffer == NULL)
       OR (lens_state_buffer == NULL)
       OR (user_prop_buffer == NULL))
     { AlMsg_debug("Msg << Exiting AlMsg_create() :: NULL POINTERS PASSED");
       return(NULL);
     };

   /* Create and initialize a message object */
   /* -------------------------------------- */
   the_msg = (Msg) Memory_allocate(sizeof(AlMsgObject));

   /* Include debugging code to register valid Msg objects (prevent bad ptr) */
   assert((AlMsg_Registry_add(AlMsg_Registry, (VOIDP) the_msg, (VOIDP) AlMsg_Registry) == Bool_TRUE));

   the_msg->name_of_msg = NULL;
   AlMsg_clear_error_code(the_msg);
   AlMsg_set_removed_flag(the_msg, Bool_FALSE);
   AlMsg_set_number_of_user_properties(the_msg, 0);
   AlMsg_set_folder(the_msg, folder_obj);
   AlMsg_set_num_rules_fired_state(the_msg, 0);
   AlMsg_set_how_many_times_moved(the_msg, 0);
   AlMsg_set_state_field_count(the_msg, 0);
   AlMsg_set_init_uprop_field_count(the_msg, 0);
   AlMsg_set_suppress_states_out(the_msg, Bool_TRUE);
   AlMsg_set_suppress_uprops_out(the_msg, Bool_TRUE);
   the_msg->moved_from = NULL;
   AlMsg_set_moved_from_state(the_msg, "");
   the_msg->msg_field_registry        = AlMsg_caseless_str_Registry_create();
   the_msg->registry_of_FC_registries = AlMsg_ptr_Registry_create();
   the_msg->user_property_registry    = AlMsg_str_Registry_create();
   the_msg->uprop_size = 0;
   the_msg->msg_buffer = msg_buffer;
   the_msg->fs_state_registry = fs_state_registry;

   /* Parse the Message from the buffer (into Darrays of FieldCores) */
   /* --------------------------------------------------------------- */
   AlMsg_read_msg(the_msg, msg_buffer);

   /* Parse the Lens State Information Buffer (into a Darray of FieldCores) */
   /* --------------------------------------------------------------------- */
   if (AlMsg_read_state(the_msg, lens_state_buffer) == Bool_FALSE)
     { AlMsg_debug("Msg << Exiting AlMsg_create() :: BAD STATE FILE");
       AlFolder_from_Msg_error_code(folder_obj, AlMsg_get_error_code(the_msg));
       /* Remove Msg object from registry of valid objects, for bad ptr trap */
       assert((AlMsg_Registry_remove(AlMsg_Registry, the_msg) == Bool_TRUE));
       return(NULL);
     };

   /* Parse the Lens User Properties Buffer (into a Darray of FieldCores) */
   /* ------------------------------------------------------------------- */
   if (AlMsg_read_user_properties(the_msg, user_prop_buffer) == Bool_FALSE)
     { AlMsg_debug("Msg << Exiting AlMsg_create() :: BAD UPROP FILE");
       AlFolder_from_Msg_error_code(folder_obj, AlMsg_get_error_code(the_msg));
       /* Remove Msg object from registry of valid objects, for bad ptr trap */
       assert((AlMsg_Registry_remove(AlMsg_Registry, the_msg) == Bool_TRUE));
       return(NULL);
     };

   AlMsg_debug("Msg << Exiting AlMsg_create()");
   return(the_msg);

 };              /* end of AlMsg_create()  */

/*****************************************************************************/
/***********************  Destroy a Message Object  **************************/
/*****************************************************************************/

Bool AlMsg_destroy(the_msg)
     Msg the_msg;
 {
   int darray_size, darray2_size, darray3_size, i, j, k;
   Darray key_darray, value_darray, field_core_darray, field_cores;
   MsgUpropField *uprop_field;
   MsgField msg_field;
   Registry the_registry;
   AlFieldCore field_core;

   AlMsg_debug(" Entering >> AlMsg_destroy()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */
   /* Remove Msg object from registry of valid objects, for bad ptr trap */
   assert((AlMsg_Registry_remove(AlMsg_Registry, the_msg) == Bool_TRUE));

   /* Create initial empty key, value, and field_core darrays */
   /* ------------------------------------------------------- */
   key_darray = Darray_create();
   value_darray = Darray_create();
   field_core_darray = Darray_create();

   /* Destroy the Msg Field Registry and its contents */
   /* ----------------------------------------------- */
   if (the_msg->msg_field_registry != NULL)
     { Registry_fetch_contents(the_msg->msg_field_registry,
                               key_darray, value_darray);
       Registry_destroy(the_msg->msg_field_registry);
       darray_size = Darray_len(value_darray);
       for (i = 0; i < darray_size; i++)
         { msg_field = (MsgField) Darray_reml(value_darray);
	   for (j = 0; j < msg_field->how_many; j++)   /* free value strings */
             Memory_free((VOIDP) (msg_field->values_array)[j]);
           Memory_free((VOIDP) Darray_reml(key_darray)); /* free key string  */
           Memory_free((VOIDP) msg_field);               /* free value array */
         };
     };

   /* Destroy the Registry of FieldCore Registries and its/their contents */
   /* ------------------------------------------------------------------- */
   if (the_msg->registry_of_FC_registries != NULL)
     { Registry_fetch_contents(the_msg->registry_of_FC_registries,
                               NULL, value_darray);
       Registry_destroy(the_msg->registry_of_FC_registries);
       darray_size = Darray_len(value_darray);
       for (i = 0; i < darray_size; i++)
         { the_registry = (Registry) Darray_reml(value_darray);
	   
           if (the_registry != NULL)
             { Registry_fetch_contents(the_registry,
                               key_darray, field_core_darray);
               Registry_destroy(the_registry);
               darray2_size = Darray_len(key_darray);
               for (j = 0; j < darray2_size; j++)
                { field_cores = (Darray) Darray_reml(field_core_darray);
                  darray3_size = Darray_len(field_cores);
                  for (k = 0; k < darray3_size; k++)
                    { field_core = (AlFieldCore) Darray_reml(field_cores);
                      AlFieldCore_delete(field_core);
		    };
                  Memory_free((VOIDP) Darray_reml(key_darray));
                };
             };
         };
     };

   /* Destroy the User Property Registry and its contents */
   /* --------------------------------------------------- */
   if (the_msg->user_property_registry != NULL)
     { Registry_fetch_contents(the_msg->user_property_registry,
                               NULL, value_darray);
       Registry_destroy(the_msg->user_property_registry);
       darray_size = Darray_len(value_darray);
       for (i = 0; i < darray_size; i++)
         { uprop_field = (MsgUpropField *) Darray_reml(value_darray);
           Memory_free((VOIDP) uprop_field->key);
           Memory_free((VOIDP) uprop_field->value);
           Memory_free((VOIDP) uprop_field);
         };
     };

   /* Registry "the_msg->fs_state_registry" is destroyed by its folder.     */
   /* Contents of "the_msg->fs_state_registry" are destroyed by its folder. */

   /* Free the char strings created for the Msg Object */
   /* ------------------------------------------------ */
   if (the_msg->moved_from != NULL)
     Memory_free((VOIDP) (the_msg->moved_from));
   if (the_msg->text_body != NULL)
     Memory_free((VOIDP) (the_msg->text_body));
   if (the_msg->name_of_msg != NULL)
     Memory_free((VOIDP) (the_msg->name_of_msg));

   /* Free Darrays used by this function. */
   /* ----------------------------------- */
   Darray_destroy(key_darray);
   Darray_destroy(value_darray);
   Darray_destroy(field_core_darray);

   /* Free the actual struct for the Msg Object */
   /* ----------------------------------------- */
   Memory_free((VOIDP) the_msg);
   AlMsg_debug(" Exiting << AlMsg_destroy()");
   return(Bool_TRUE);

 };             /* end of AlMsg_destroy()  */

/*****************************************************************************/
/*********  Print a Message Object (in English-like form) to a file **********/
/*****************************************************************************/

NORET AlMsg_print_self(the_msg, outfile)
     Msg the_msg;
     FILE *outfile;
 {
   int i, j, darray_size;
   Darray key_darray, value_darray;
   MsgField msg_field;
   MsgUpropField *uprop_field;

   AlMsg_debug("Msg >> Entering AlMsg_print_self()");

   /* First use debugging code to trap bad pointers, if turned on */
   /* ----------------------------------------------------------- */
   assert(outfile != NULL);            /* outfile exists         */
   assert((AlMsg_Registry_get(AlMsg_Registry, the_msg) != NULL));  /* Msg exists */

   /* Create initial empty key, value, and field_core darrays */
   /* ------------------------------------------------------- */
   key_darray = Darray_create();
   value_darray = Darray_create();

   /* Print out a header */
   /* ------------------ */
   fprintf(outfile, "Message object (&%d) contains:\n", (int) the_msg);

   /* Print out Name Spaces */
   /* --------------------- */
   fprintf(outfile, "   ----------- \n");
   fprintf(outfile, "   Name Spaces \n");
   fprintf(outfile, "   ----------- \n");
   fprintf(outfile, "msg_field_registry (ptr) = %d\n",
	  (int) the_msg->msg_field_registry);
   fprintf(outfile, "registry_of_FC_registries (ptr) =  %d\n",
	  (int) the_msg->registry_of_FC_registries);
   fprintf(outfile, "user_property_registry (ptr) = %d\n",
	  (int) the_msg->user_property_registry);
   fprintf(outfile, "fs_state_registry (ptr) =  %d\n",
	  (int) the_msg->fs_state_registry);

   /* Print out Folder info */
   /* --------------------- */
   fprintf(outfile, "   ----------- \n");
   fprintf(outfile, "   Folder Info \n");
   fprintf(outfile, "   ----------- \n");
   fprintf(outfile, "parent_folder (ptr) =  %d\n",
	  (int) the_msg->parent_folder);
   fprintf(outfile, "msg_buffer (ptr) =  %d\n",
	  (int) the_msg->msg_buffer);

   /* Print out User Propterty info */
   /* ----------------------------- */
   fprintf(outfile, "   ------------------- \n");
   fprintf(outfile, "   User Propterty info \n");
   fprintf(outfile, "   ------------------- \n");
   fprintf(outfile, "uprop_size (ptr) =  %d\n", the_msg->uprop_size);

   /* Print out State info */
   /* -------------------- */
   fprintf(outfile, "   ---------- \n");
   fprintf(outfile, "   State info \n");
   fprintf(outfile, "   ---------- \n");
   if (the_msg->name_of_msg != NULL)
     fprintf(outfile, "name_of_msg = %d\n", the_msg->name_of_msg);
   else
     fprintf(outfile, "name_of_msg = NULL\n");
   fprintf(outfile, "number_of_fields = %d\n", the_msg->number_of_fields);
   fprintf(outfile, "msg_line_length = %d\n", the_msg->msg_line_length);
   fprintf(outfile, "msg_char_length = %d\n", the_msg->msg_char_length);
   fprintf(outfile, "body_line_length = %d\n", the_msg->body_line_length);
   fprintf(outfile, "body_char_length = %d\n", the_msg->body_char_length);
   fprintf(outfile, "unprintables_in_body = %s\n",
	  AlMsg_Bool_to_str(the_msg->unprintables_in_body));
   fprintf(outfile, "error_code = %d\n", the_msg->error_code);
   fprintf(outfile, "number_of_user_properties = %d\n", 
	  the_msg->number_of_user_properties);
   fprintf(outfile, "state_field_count = %d\n", the_msg->state_field_count);
   fprintf(outfile, "init_uprop_field_count = %d\n",
	   (int) the_msg->init_uprop_field_count);
   fprintf(outfile, "suppress_states_out = %s\n",
	  AlMsg_Bool_to_str(AlMsg_get_suppress_states_out(the_msg)));
   fprintf(outfile, "suppress_uprops_out = %s\n",
	  AlMsg_Bool_to_str(AlMsg_get_suppress_uprops_out(the_msg)));

   /* Print out States that are purely for Data Collection */
   /* ---------------------------------------------------- */
   fprintf(outfile, "   ---------------------- \n");
   fprintf(outfile, "   Data Collection States \n");
   fprintf(outfile, "   ---------------------- \n");
   fprintf(outfile, "num_rules_fired = %d\n", the_msg->num_rules_fired);
   fprintf(outfile, "how_many_times_moved = %d\n",
	  the_msg->how_many_times_moved);
   fprintf(outfile, "moved_from = \"%s\"\n", the_msg->moved_from);

   /* Print out the User Property Registry's contents */
   /* ----------------------------------------------- */
   if (the_msg->user_property_registry != NULL)
     { 
       fprintf(outfile, "   -------------------------------- \n");
       fprintf(outfile, "   All User Property Fields follow: \n");
       fprintf(outfile, "   -------------------------------- \n");
       Registry_fetch_contents(the_msg->user_property_registry,
               key_darray, /* DEBUG. Was NULL, hack to get around Jeffs bugs */
               value_darray);
       darray_size = Darray_len(value_darray);
       for (i = 0; i < darray_size; i++)
         { uprop_field = (MsgUpropField *) Darray_reml(value_darray);
	   Darray_reml(key_darray); /* DEBUG hack to get around Jeffs bugs */
	   fprintf(outfile, "%s\n[%d] %s\n", uprop_field->key, 0,
                    uprop_field->value);
         };
     };

   /* Print the Msg Field Registry's contents */
   /* --------------------------------------- */
   if (the_msg->msg_field_registry != NULL)
     { 
       fprintf(outfile, "   ---------------------- \n");
       fprintf(outfile, "   All Msg Fields follow: \n");
       fprintf(outfile, "   ---------------------- \n");
       Registry_fetch_contents(the_msg->msg_field_registry,
                               key_darray, value_darray);
       darray_size = Darray_len(value_darray);
       for (i = 0; i < darray_size; i++)
         { msg_field = (MsgField) Darray_get(value_darray, (unsigned) i);
           fprintf(outfile, "%s\n",
                   (char *) Darray_get(key_darray, (unsigned) i));
	   for (j = 0; j < msg_field->how_many; j++)  /* print value strings */
             fprintf(outfile, "[%d]%s\n", j, msg_field->values_array[j]);
         };
     };

   /* Print out Body (text) of the Message */
   /* ------------------------------------ */
   fprintf(outfile, "   ------------------------ \n");
   fprintf(outfile, "   Body of Message Follows: \n");
   fprintf(outfile, "   ------------------------ \n");
   fprintf(outfile, "%s\n\n", the_msg->text_body);

   /* Free Darrays used by this function. */
   /* ----------------------------------- */
   Darray_destroy(key_darray);
   Darray_destroy(value_darray);

   AlMsg_debug("Msg << Exiting AlMsg_print_self()");

 };                 /* end of AlMsg_print_self()  */
