#include "al.h"
#include "mh_folderP.h"

/****************************************************/
/* This file contains the code for the MH Folder    */
/* object, used in Athena Information Lens.         */
/* Written Brian R. Gardner, 1988.                  */
/****************************************************/


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

Bool AlMHfolder_isinteger(numstr)
     char *numstr;
 {  
   int len, i;

   AlMHfolder_debug2(" Entering >> AlMHfolder_isinteger(): numstr =", numstr);
   /* Given a str, return Bool_TRUE if it is an integer */
   /* ------------------------------------------------- */
   len = strlen(numstr);
   for(i = 0; ((i < len) && isdigit(numstr[i])); i++);
   AlMHfolder_debug2(" Exiting << AlMHfolder_isinteger(): returning =>", 
                     AlMHfolder_bool_str((i >= len) ? Bool_TRUE : Bool_FALSE));
   return((i >= len) ? Bool_TRUE : Bool_FALSE);
 };

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

char *AlMHfolder_filename_from_path(pathname)
     char *pathname;
 {
   char *filename;

   for(filename = (pathname + strlen(pathname));
       filename > pathname;
       filename--)
     if (*filename == '/')
       return(filename + 1);

   return(pathname);
 };

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

Bool AlMHfolder_is_MHfile(pathname)
     char *pathname;
 {
   return(AlMHfolder_isinteger(AlMHfolder_filename_from_path(pathname)));
 };

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

Bool AlMHfolder_sh3(sh_string, s1, s2)
     char *sh_string, *s1, *s2;
 {
   char *command;
   Bool flag;
   FILE *fp;

   AlMHfolder_debug(" Entering >> AlMHfolder_AlMHfolder_sh3()");

   /* Create a buffer in which to load the control string and 2 str params. */
   /* --------------------------------------------------------------------- */
   command = (char *) Memory_allocate((long) strlen(sh_string) + 1
				      + strlen(s1) + strlen(s2));

   /* Load the shell command into a buffer. */
   /* ------------------------------------- */
   sprintf(command, sh_string, s1, s2);

   /* Invoke the command in a shell process */
   /* ------------------------------------- */
   fp = popen(command, "w+");
   AlMHfolder_debug2("DEBUG: shell command =>", command);
   if (fp == NULL)
     flag = Bool_FALSE;
   else
     flag = Bool_TRUE;
   fflush(fp);
   pclose(fp);
   Memory_free((VOIDP) command);

   AlMHfolder_debug(" Exiting << AlMHfolder_AlMHfolder_sh3()");

   return(flag);
 };                       /* end of AlMHfolder_sh3()  */

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

FILE *AlMHfolder_pipe3(sh_string, s1, s2)
     char *sh_string, *s1, *s2;
 {
   char *command;
   FILE *fp;

   AlMHfolder_debug(" Entering >> AlMHfolder_AlMHfolder_pipe3()");

   /* Create a buffer in which to load the control string and 2 str params. */
   /* --------------------------------------------------------------------- */
   command = (char *) Memory_allocate((long) strlen(sh_string) + 1
				      + strlen(s1) + strlen(s2));

   /* Load the shell command into a buffer. */
   /* ------------------------------------- */
   sprintf(command, sh_string, s1, s2);

   /* Invoke the command in a shell process */
   /* ------------------------------------- */
   fp = popen(command, "w+");
   AlMHfolder_debug2("  In AlMHfolder_pipe3 : pipe command =>", command);
   Memory_free((VOIDP) command);

   AlMHfolder_debug(" Exiting << AlMHfolder_AlMHfolder_pipe3()");

   return(fp);
 };                       /* end of AlMHfolder_pipe3()  */

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

Bool AlMHfolder_file_info(filename, size, directory_flag)
     char *filename;
     int *size;
     Bool *directory_flag;
 {
   struct stat stbuf;

   AlMHfolder_debug(" Entering >> AlMHfolder_file_info()");

   /* Get important info about a file */
   /* ------------------------------- */
   if (stat(filename, &stbuf) == -1)
     { AlMHfolder_debug(" Exiting << AlMHfolder_file_info() : failing");
       return(Bool_FALSE);
     }
   else
     { *size = stbuf.st_size;
       if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
	 *directory_flag = Bool_TRUE;
       else
	 *directory_flag = Bool_FALSE;
       AlMHfolder_debug(" Exiting << AlMHfolder_file_info()");
       return(Bool_TRUE);
     };
 };                 /* end of AlMHfolder_file_info()  */

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

struct dir_ob *AlMHfolder_create_directory(folder_name, name)
     char *folder_name;  /* mh name of folder */
     char *name;         /* filename (path) of folder */
 { 
   struct dir_ob *dir_ob;
   long min_bufsize;
   FILE *fp;
   DIR *dp;     /* directory pointer */


   /* Create an mh directory object to keep track of what files are in use, */
   /* etc.. Open a Unix directory file, specified by name.                  */
   /* If (name == NULL) then use the standard input file (piping input).    */
   /* --------------------------------------------------------------------- */
   AlMHfolder_debug(" Entering >> AlMHfolder_create_directory()");
   AlMHfolder_debug2("       In AlMHfolder_create_directory() : name=>",
		     ((name == NULL) ? "NULL" : name));
   AlMHfolder_debug2("       In AlMHfolder_create_directory() : folder_name=>",
		     ((folder_name == NULL) ? "NULL" : folder_name));

   if (name != NULL)
     min_bufsize = strlen(name) + BUFSIZE + 3;
   else
     min_bufsize = BUFSIZE + 3;
   dir_ob = (struct dir_ob *)
             Memory_allocate(sizeof(struct dir_ob));

   /* Create the folder_string, used to specify a folder to mh.           */
   /* That is the tail portion of a pathname following the mhpath prefix. */
   /* ------------------------------------------------------------------- */
   dir_ob->folder_string = (char *)
                   Memory_allocate((long) strlen(folder_name) + 1);
   strcpy(dir_ob->folder_string, folder_name);

   /* Create the name buffer. This is the full pathname, plus extra space, */
   /* into which each filename may be appended/deleted (workspace buffer). */
   /* -------------------------------------------------------------------- */
   dir_ob->name_buffer = (char *) Memory_allocate(min_bufsize);
   strcpy(dir_ob->name_buffer, name);

   /* Create the pathname. This holds the full pathname (no filenames),    */
   /* to be used later when using "opendir". Storing it here allows us to  */
   /* minimize the number of concurrently opened directories.              */
   /* -------------------------------------------------------------------- */
   dir_ob->pathname = (char *) Memory_allocate(strlen(name) + 1);
   strcpy(dir_ob->pathname, name);
   dir_ob->opened = Bool_FALSE;
   dir_ob->bufsize = min_bufsize;
   dir_ob->nbp = dir_ob->name_buffer + ((name == NULL) ? 0 : strlen(name));
   dir_ob->next = NULL;

   dp = opendir(name);             /* Compatible ?? */
   if (dp == NULL)  /* failed first attempt to open */  /* Compatible ?? */
     { 
       if (name != NULL)
          {  /* attempt to create folder */
 	    AlMHfolder_sh3("%s +%s -create 2> /dev/null", 
		    AlMH_RCVSTORE_PATH, 
                   (name[0] == '+') ? &(name[1]) : name);
/*	    fp = AlMHfolder_pipe3("%s +%s -create", */
/*	    close(fp);                              */
	  };
       dp = opendir(name);  /* try to open new folder */    /* Compatible ?? */
     };
   if (dp == NULL)  /* failed attempt to open */  /* Compatible ?? */
     { Memory_free((VOIDP) dir_ob->name_buffer);
       Memory_free((VOIDP) dir_ob->folder_string);
       Memory_free((VOIDP) dir_ob->pathname);
       Memory_free((VOIDP) dir_ob);
       AlMHfolder_debug(" Exiting << AlMHfolder_create_directory() : failing");
       return(NULL);
     }
   else
     { *(dir_ob->nbp++) = '/';    /* add slash to directory name */
       *(dir_ob->nbp) = '\0';     /* terminate string for directory name */
       closedir(dp);              /* close for now, reopen later when needed */
       AlMHfolder_debug(" Exiting << AlMHfolder_create_directory()");
       return(dir_ob);
     };
 };                   /* end of AlMHfolder_create_directory()  */

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

NORET AlMHfolder_close_directory(dir_ob)
     struct dir_ob *dir_ob;
 {
   AlMHfolder_debug(" Entering >> AlMHfolder_close_directory()");

   if (fileno(stdin) != dir_ob->fd)
     { /* close(dir_ob->fd); */
       closedir(dir_ob->dirp);            /* Compatible ? */
     };
   if (dir_ob->name_buffer != NULL)
     { AlMHfolder_debug2("       In AlMHfolder_close_directory() : name=>",
			 dir_ob->name_buffer);
       Memory_free((VOIDP) dir_ob->name_buffer);
     };
   if (dir_ob->pathname != NULL)
     { AlMHfolder_debug2("       In AlMHfolder_close_directory() : pathname=>",
			 dir_ob->pathname);
       Memory_free((VOIDP) dir_ob->pathname);
     };
   if (dir_ob->folder_string != NULL)
     { AlMHfolder_debug2("   In -- AlMHfolder_close_directory()folder_string=",
                         dir_ob->folder_string);
       Memory_free((VOIDP) (dir_ob->folder_string)); /* free old folder name */
     };
   Memory_free((VOIDP) dir_ob);            /* free folder's directory object */

   AlMHfolder_debug(" Exiting << AlMHfolder_close_directory()");

 };                   /* end of AlMHfolder_close_directory()  */

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

char *AlMHfolder_get_next_filename(dir_ob)
     struct dir_ob *dir_ob;
 {
   struct direct *dirsp;
   char *nep, *temp;
   DIR *dp;     /* directory pointer */
   int i;

   AlMHfolder_debug(" Entering >> AlMHfolder_get_next_filename()");
   assert(dir_ob != NULL);

   if (dir_ob->opened != Bool_TRUE)
     { /* If directory hasn't been opened, then open it now. */
       /* -------------------------------------------------- */
       dp = opendir(dir_ob->pathname);             /* Compatible ?? */
       if (dp == NULL)
         { Al_fatal_error1(
            "ERROR: Directory has dissappeared while it was being used:%s\n",
            dir_ob->pathname);
	 };
       dir_ob->fd = dp->dd_fd;         /* Compatible ?? */
       dir_ob->dirp = dp;              /* Compatible ?? */
       dir_ob->opened = Bool_TRUE;
     };
   if (dir_ob->nbp + CDIRSIZ + 2 >= dir_ob->name_buffer + dir_ob->bufsize)
     { /* Name too long, resize buffer to accommodate it. */
       /* ----------------------------------------------- */
       AlMHfolder_debug("    In AlMHfolder_get_next_filename() : resizing");
       temp = dir_ob->name_buffer;
       if (dir_ob->name_buffer == NULL)
         dir_ob->name_buffer = (char *) 
	           Memory_allocate((long)
                       (dir_ob->bufsize += (CDIRSIZ + CDIRSIZ + 4)));
       else
	 dir_ob->name_buffer = (char *) 
	           Memory_reallocate((VOIDP) dir_ob->name_buffer, (long)
			         (dir_ob->bufsize += (CDIRSIZ + CDIRSIZ + 4)));
       /* Re-direct nbp to point into the new buffer area */
       /* ----------------------------------------------- */
       dir_ob->nbp = dir_ob->name_buffer + (dir_ob->nbp - temp);
     };
   if ((dirsp = readdir(dir_ob->dirp)) != NULL)    /* Compatible ? */
     { if (dirsp->d_ino == 0)      /* slot not in use */
	 { AlMHfolder_debug(
	      " Exiting << AlMHfolder_get_next_filename() : slot not in use");
	   return(AlMHfolder_get_next_filename(dir_ob));
	 };
       if (* dirsp->d_name == '.') /* skip over self, parent, & hidden files */
	 { AlMHfolder_debug(
           " Exiting << AlMHfolder_get_next_filename() : self or parent (ok)");
	   return(AlMHfolder_get_next_filename(dir_ob));
	 };
       for (i=0, nep=(dir_ob->nbp);
	    ((i < CDIRSIZ) && (dirsp->d_name[i] != '\0')); i++)
	 *(nep++) = dirsp->d_name[i];
       *(nep++) = '\0';
       AlMHfolder_debug2("      buffered filename =>", dir_ob->name_buffer);
       AlMHfolder_debug(" Exiting << AlMHfolder_get_next_filename()");
       return(dir_ob->name_buffer);
     }
   else
     { AlMHfolder_debug(" Exiting << AlMHfolder_get_next_filename() : done");
       return(NULL);
     };
 };                  /* end of AlMHfolder_get_next_filename()  */

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

FILE *AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr, write_flag,
				      recursion_flag)
     char **fnp;                       /* ptr to the filename variable */
     int *fsizep;                      /* ptr to size of file variable */
     struct dir_ob **dir_ob_ptr; /* ptr to the dir_ob variable   */
     Bool write_flag;                  /* If write access to file is needed */
     Bool recursion_flag;
 {
   FILE *fp;
   char *filename, access[3], *sub_folder, *new_folder_string;
   Bool directory_flag;
   struct dir_ob *dir_ob;
   struct dir_ob *subdir_ob, *temp_dir_p;

   AlMHfolder_debug(" Entering >> AlMHfolder_get_next_file()");

   dir_ob = *dir_ob_ptr;
   if ((filename = AlMHfolder_get_next_filename(dir_ob)) != NULL)
     { if (AlMHfolder_file_info(filename, fsizep, &directory_flag)
	   != Bool_TRUE)
	 { Al_warning2("Warning: Unable to stat %s . Skipping %s .\n",
			filename, filename);
	   AlMHfolder_debug(
		       " Exiting << AlMHfolder_get_next_file() : cant stat");
	   return(AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr, write_flag,
				      recursion_flag));
	 }
       else
	 { if (directory_flag == Bool_TRUE)
	     { /* Handle a subdirectory */
	       /* --------------------- */
	       if (recursion_flag == Bool_TRUE)
		 { /* Use subdirectories */
		   /* ------------------ */
                   sub_folder = AlMHfolder_filename_from_path(filename);
                   new_folder_string = (char *)
                          Memory_allocate((long) strlen(sub_folder)
					  + strlen(dir_ob->folder_string)
					  + 3);
                   /* new sub-folder name = oldname + '/' + newname */
		   /* --------------------------------------------- */
		   strcpy(new_folder_string, dir_ob->folder_string);
                   /* create the new sub-folder name */
		   new_folder_string[strlen(dir_ob->folder_string)] = '/';
		   strcpy((new_folder_string + 1
			   + strlen(dir_ob->folder_string)),
			  sub_folder);
		   subdir_ob = AlMHfolder_create_directory(new_folder_string,
							   filename);
		   Memory_free((VOIDP) new_folder_string);
		   if (subdir_ob == NULL)
		     { Al_warning2(
			      "Warning: Unable to open %s . Skipping %s .\n",
			      filename, filename);
		       AlMHfolder_debug(
 		        " Exiting << AlMHfolder_get_next_file() : cant open");
		       return(AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr,
					write_flag, recursion_flag));
		     }
		   else    /* push a subdirectory onto the stack */
		     { temp_dir_p = dir_ob->next;
                       dir_ob->next = subdir_ob;
                       subdir_ob->next = temp_dir_p;
		       AlMHfolder_debug(
 		       " Exiting << AlMHfolder_get_next_file() : push subdir");
		       return(AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr,
					write_flag, recursion_flag));
		     };
		 }
	       else     /* ignore subdirectories */
		 { AlMHfolder_debug(
 		     " Exiting << AlMHfolder_get_next_file() : ignore subdir");
		   return(AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr, 
					write_flag, recursion_flag));
		 };
	     }
	   else
	     { /* Handle a file */
	       /* ------------- */
	       if (AlMHfolder_is_MHfile(filename) != Bool_TRUE)
		 { /* mh filenames are always a number */
		   AlMHfolder_debug(
                    " Exiting << AlMHfolder_get_next_file() : recursing");
		   return(AlMHfolder_get_next_file(fnp,
				      fsizep, dir_ob_ptr, write_flag,
				      recursion_flag));
		 };
	       if (write_flag == Bool_TRUE)
		 strcpy(access, "r+");
	       else
	         strcpy(access, "r");
	       if ((fp = fopen(filename, access)) == NULL)
		 { Al_warning2("Warning: Can't open file \"%s\" for %s.\n",
				  filename,
				  (write_flag == Bool_TRUE)
				  ? "writing" : "reading");
		   AlMHfolder_debug(
 		    " Exiting << AlMHfolder_get_next_file() : cant open file");
		   return(AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr, 
					write_flag, recursion_flag));
		 }
	       else
		 { *fnp = filename;
		   AlMHfolder_debug(
 		    " Exiting << AlMHfolder_get_next_file() : successful");
		   return(fp);
		 };
	     };
	 };
     }
   else
     { /* Finished processing files in this directory, pop stack to next dir */
       /* ------------------------------------------------------------------ */
       if ((*dir_ob_ptr = dir_ob->next) != NULL)
	 { AlMHfolder_close_directory(dir_ob);
	   AlMHfolder_debug(
 	     " Exiting << AlMHfolder_get_next_file() : pop subdir");
	   return(AlMHfolder_get_next_file(fnp, fsizep, dir_ob_ptr, 
					write_flag, recursion_flag));
	 }
       else
	 { AlMHfolder_close_directory(dir_ob);
	   AlMHfolder_debug(
 	   " Exiting << AlMHfolder_get_next_file() : done all files and dir.");
	   return(NULL);    /* stack empty, all files/directories are done */
	 };
     };
   };               /* end of AlMHfolder_get_next_file()  */
     
/****************************************************************************/

Bool AlMHfolder_write_msg(filename, the_msg)
     char *filename;
     Msg the_msg;
 {
   char *buffer;
   FILE *outfile;

   AlMHfolder_debug(" Entering >> AlMHfolder_write_msg()");

#if (AlMHfold_DEBUG == 1)      /* More DEBUG stuff */
    AlMsg_print_self(the_msg, stdout);
#endif

   if ((outfile = fopen(filename, "w")) == NULL)
     { Al_warning2("%s :: Unable to write mh msg to file: %s\n",
		      "MH Folder", filename);
       AlMHfolder_debug(" Exiting << AlMHfolder_write_msg() : failing");
       return(Bool_FALSE);
     };
   if (AlMsg_get_suppress_states_out(the_msg) == Bool_FALSE)
     { /* Output of state info is not suppressed, so write it out to file. */
       /* ---------------------------------------------------------------- */
       buffer = AlMsg_write_states_to_buffer(the_msg);
       fputs(buffer, outfile);

#if (AlMHfold_DEBUG == 1)      /* More DEBUG stuff */
       fputs(buffer, stdout);
#endif

       Memory_free((VOIDP) buffer); 
     };
   if (AlMsg_get_suppress_uprops_out(the_msg) == Bool_FALSE)
     { /* Output of uprop info is not suppressed, so write it out to file. */
       /* ---------------------------------------------------------------- */
       buffer = AlMsg_write_uprops_to_buffer(the_msg);
#if (AlMHfold_DEBUG == 1)      /* More DEBUG stuff */
       fputs(buffer, stdout);
#endif

       fputs(buffer, outfile);
       Memory_free((VOIDP) buffer); 
     };

   /* Now write out the original text of the message. */
   /* ----------------------------------------------- */
   buffer = AlMsg_get_msg_buffer(the_msg);
   if (strlen(buffer) == 0)
     { Al_warning2(
          " %s Warning: Message of zero length put in file \"%s\" .\n",
			"FolderMH ::", filename);
#if (AlMHfold_DEBUG == 1)      /* More DEBUG stuff */
       fputs(buffer, stdout);
#endif

     };
   fputs(buffer, outfile);
   fclose(outfile); 

   AlMHfolder_debug(" Exiting << AlMHfolder_write_msg()");
   return(Bool_TRUE);
 };            /* end of AlMHfolder_write_msg()  */

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

Bool AlMHfolder_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 AlMHfolder_strncmp()  */

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

char *AlMHfolder_move_to_next_mline(index, outer_key)
     char *index, *outer_key;
 { char *result, *key, *encoded_buffer;
   char *next_buffer_index;
   int  line_count = 0, char_count = 0;

   AlMHfolder_debug(" Entering >> AlMHfolder_move_to_next_mline()");

   next_buffer_index = index;

   /* Check for an empty buffer, first. If empty, then all finished. */
   /* -------------------------------------------------------------- */
   if (*index == '\0')
     return(index);       /* Reached End-Of-File (end of string) */

   /* Make sure that the next field is has the outer key requested. */
   /* ------------------------------------------------------------- */
   key = AlMsg_readkey(&next_buffer_index, &line_count, &char_count);
   if (key == NULL)    /* No key fields to read. Finished reading */
     { AlMHfolder_debug(
	" Exiting << AlMHfolder_move_to_next_mline() : no keys left (ok)");
       return(index);
     }
   else if (strcmp(key, outer_key) != 0)   /* didn't match. */
     { Memory_free((VOIDP) key);           /* wasn't the requested key (ok) */
       AlMHfolder_debug(
	" Exiting << AlMHfolder_move_to_next_mline(): not requested key (ok)");
       return(index);
     }
   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 */
          { /* key had empty value, not normal, but ok */
            AlMHfolder_debug(
	      " Exiting << AlMHfolder_move_to_next_mline(): empty field (ok)");
            return(index);
          };
       encoded_buffer = AlMsg_readvalue(&next_buffer_index,
					 &line_count, &char_count);
       Memory_free((VOIDP) encoded_buffer);
     };
   return(next_buffer_index);

 };                /* end of AlMHfolder_move_to_next_mline()  */

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

NORET AlMHfolder_parse(file_buffer, msg_buffer, lens_state_buffer,
		      user_prop_buffer, msg_name)
     char *file_buffer;
     char **msg_buffer, **lens_state_buffer, **user_prop_buffer;
     char *msg_name;                       /* filename of this MH msg */
 {
     char *start_index, *index, *old_index;
     int  prefix_len;

      AlMHfolder_debug(" Entering >> AlMHfolder_parse()");

     /* Parse State info */
     /* ---------------- */
     prefix_len = strlen(AlMsg_STATE_PREFIX);
     start_index = file_buffer;
     index = file_buffer;
     if (AlMHfolder_strncmp(file_buffer, AlMsg_STATE_PREFIX, prefix_len)
	 == Bool_FALSE)
       { /* no state info exists in file */
	 *lens_state_buffer = "";
       }
     else
       { old_index = index;
         for (index = AlMHfolder_move_to_next_mline(start_index,
						    AlMsg_STATE_PREFIX);
	      (old_index != index);
	      index = AlMHfolder_move_to_next_mline(index, AlMsg_STATE_PREFIX))
	   old_index = index;
	 *lens_state_buffer = start_index;
          /* Replace last '\n' with '\0' to terminate the (now) state buffer */
	 *(index - 1) = '\0';
       };

     /* Parse User Property info */
     /* ------------------------ */
     prefix_len = strlen(AlMsg_UPROP_PREFIX);
     start_index = index;
     if (AlMHfolder_strncmp(index, AlMsg_UPROP_PREFIX, prefix_len)
	 == Bool_FALSE)
       { /* no uprop info exists in file */
	 *user_prop_buffer = "";
       }
     else
       { old_index = index;
	 for (index = AlMHfolder_move_to_next_mline(start_index,
						    AlMsg_UPROP_PREFIX);
	      (old_index != index);
	      index = AlMHfolder_move_to_next_mline(index, AlMsg_UPROP_PREFIX))
	   old_index = index;
	 *user_prop_buffer = start_index;
          /* Replace last '\n' with '\0' to terminate the (now) uprop buffer */
	 *(index - 1) = '\0';
       };

     /* Set ptr to buffer containing rest of the msg. */
     /* --------------------------------------------- */
     *msg_buffer = index;
#if (AlMHfold_DEBUG == 1)
   printf("*****************AlMHfolder_parse BUFFERS follow***************\n");
   printf("file_buffer FOLLOWS:\n%s\n", file_buffer);
   printf("msg_buffer FOLLOWS:\n%s\n", *msg_buffer);
   printf("lens_state_buffer FOLLOWS:\n%s\n", *lens_state_buffer);
   printf("user_prop_buffer FOLLOWS:\n%s\n", *user_prop_buffer);
   printf("***********end of AlMHfolder_parse BUFFERS follow**************\n");
#endif
     AlMHfolder_debug(" Exiting << AlMHfolder_parse() : successful");

 };                  /* end of AlMHfolder_parse()  */

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

NORET AlMHfolder_get_next_msg(mh_d, msg_buf_ptr, state_buf_ptr,
			      uprop_buf_ptr, fs_state_registry,
			      do_nested_folders, no_external_state_usage,
			      no_external_uprop_usage, prev_msg)
     VOIDP mh_d;
     char **msg_buf_ptr, **state_buf_ptr, **uprop_buf_ptr;
     Registry fs_state_registry;
     Bool do_nested_folders, no_external_state_usage, no_external_uprop_usage;
     Msg prev_msg;
 {
     AlMHdata *mh_data;
     Bool write_flag;
     int file_size, nchars;
     FILE *fp;
     AlFolder the_folder;

     AlMHfolder_debug(" Entering >> AlMHfolder_get_next_msg()");

     mh_data = (AlMHdata *) mh_d;
     if (prev_msg != NULL)
       { /* Set some useful pointers and flags */
         /* ---------------------------------- */
	 the_folder = AlMsg_get_folder(prev_msg);

         /* No writing is needed, if:                                    */
         /*    There were no state read and none to write                */
         /*    and there were no user properties read and none to write. */
	 /* ------------------------------------------------------------ */
         write_flag = ((AlMsg_get_state_field_count(prev_msg) == 0)
		   && (AlMsg_get_suppress_states_out(prev_msg) == Bool_TRUE)
		   && (AlMsg_get_init_uprop_field_count(prev_msg) == 0)
                   && ((AlMsg_get_suppress_uprops_out(prev_msg) == Bool_TRUE)
                       OR (AlMsg_get_num_perm_uprops(prev_msg) == 0)))
                  ? Bool_FALSE
		  : Bool_TRUE ;
       }
     else
       { the_folder = NULL;
         write_flag = Bool_FALSE;    /* No msg to even write out yet. */
       };

     AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 1");

     /* Write out any states or uprops that are needed, with last msg used */
     /* ------------------------------------------------------------------ */
     if (prev_msg != NULL)
       { AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 2");
	 if ((write_flag == Bool_TRUE)
	     && (AlMsg_get_removed_flag(prev_msg) != Bool_TRUE))
	   AlMHfolder_write_msg(mh_data->filename, prev_msg);
       };

     /* First, free any spare memory from last Msg used. */
     /* ------------------------------------------------ */
     /* NULL out ptrs into file buffer, just to keep state of world clean */
     *msg_buf_ptr = NULL; 
     *state_buf_ptr = NULL; 
     *uprop_buf_ptr = NULL; 
     if (mh_data->file_buffer != NULL)
       { Memory_free((VOIDP) mh_data->file_buffer);
	 mh_data->file_buffer = NULL;
       };

     AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 3");

     /* Get the file pointer to the next msg file to be processed */
     /* --------------------------------------------------------- */
     write_flag = Bool_FALSE;
     fp = AlMHfolder_get_next_file( &(mh_data->filename),
		       &file_size,
		       &(mh_data->dir_ob_list), /* push-down stack of dir_ob */
		       write_flag, do_nested_folders);

     AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 4");

     /* Check for completion */
     /* -------------------- */
     if (fp == NULL)
       { /* Finished. All msgs in this folder are done. */
	 /* ------------------------------------------- */
	 AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 5");
       } 
     else
       {
         /* Read in Message file and parse it into the 3 necessary buffers */
         /* -------------------------------------------------------------- */
         mh_data->file_buffer = (char *) Memory_allocate((long) file_size + 2);
         nchars = fread(mh_data->file_buffer, sizeof(char), file_size, fp);
	 *(mh_data->file_buffer + file_size) = '\0';
         fclose(fp);           /* close up the message file */
#if (AlMHfold_DEBUG == 1)
   printf("**************AlMHfolder_get_next_msg info follows*************\n");
   printf(" file_size = %d, nchars = %d, errno = %d\n",
	  file_size, nchars, errno);
   printf("********end of AlMHfolder_get_next_msg info follows************\n");
#endif
	 AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 6");
	 AlMHfolder_parse(mh_data->file_buffer, msg_buf_ptr, state_buf_ptr,
			  uprop_buf_ptr, mh_data->filename);
	 if (mh_data->dir_ob_list != NULL)
           { if (((mh_data->dir_ob_list)->folder_string != NULL)
		 && (the_folder != NULL))
               AlFolder_reset_name(the_folder,
				   (mh_data->dir_ob_list)->folder_string);
	   };
	 AlMHfolder_debug("        In  AlMHfolder_get_next_msg() : checkpt 7");
       };

     AlMHfolder_debug(" Exiting << AlMHfolder_get_next_msg()");
   };                    /* end of AlMHfolder_get_next_msg()  */
     
/****************************************************************************/

VOIDP AlMHfolder_create_from_file(folder_string, open_status)
     char *folder_string;
     int open_status;
 { struct mh_data_struct *data_ob;
   FILE *fp;
   char *filename, *command;
   int c, i, f_size;

   AlMHfolder_debug(" Entering >> AlMHfolder_create_from_file()");

   f_size = 80 + strlen(folder_string);
   filename = (char *) Memory_allocate((long) f_size);

   /* Get the full path and directory name for the mh folder from mh */
   /* -------------------------------------------------------------- */
   command  = (char *) Memory_allocate((long) f_size);
   sprintf(command, "mhpath +%s",
      (folder_string[0] == '+') ? &(folder_string[1]) : folder_string);
   fp = popen(command, "r");
   if (fp == NULL)
     { Al_fatal_error1("Folder :: Unable to identify folder path for: %s\n",
		    folder_string);
     };
   c = getc(fp);
   for(i = 0; ((c != EOF) && (c != '\n')); i++)
     { if ((i + 1) >= f_size)
	 filename = (char *) 
	            Memory_reallocate((VOIDP) filename, (long) (f_size += 80));
       filename[i] = (char) c;
       c = getc(fp);
     };
   filename[i] = '\0';
   pclose(fp);
   AlMHfolder_debug2("      expanded filename =>", filename);
   Memory_free(command);

   data_ob = (AlMHdata *) Memory_allocate(sizeof(AlMHdata));
   data_ob->file_buffer = NULL;
   data_ob->filename = filename;               /* filename of current folder */
   /* Set first entry for push-down stack of dir_ob */
   data_ob->dir_ob_list = AlMHfolder_create_directory(folder_string, filename);

   AlMHfolder_debug(" Exiting << AlMHfolder_create_from_file()");
   return((VOIDP) data_ob);
 };                      /* end of AlMHfolder_create_from_file()  */

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

Bool AlMHfolder_remove_cur_msg(data_ob, the_msg)
     VOIDP data_ob;
     Msg the_msg;
 {
   AlMHdata *mh_data;
   Bool flag;
   struct dir_ob *top_dir_ob; /* top dir_ob on push-down stack of dir_ob's */

   AlMHfolder_debug(" Entering >> AlMHfolder_remove_cur_msg()");

   assert(data_ob != NULL);
   mh_data = (AlMHdata *) data_ob;
   assert(mh_data->dir_ob_list != NULL);
   assert(mh_data->dir_ob_list->folder_string != NULL);

   if (mh_data->dir_ob_list == NULL)
     return(Bool_FALSE);     /* There is no validly opened directory. */

   if (AlMsg_get_removed_flag(the_msg) == Bool_TRUE)
     return(Bool_TRUE);     /* Message was already removed. */

   top_dir_ob = mh_data->dir_ob_list;
   flag = AlMHfolder_sh3("rmm +%s %s",
                         ((*(top_dir_ob->folder_string) == '+')
                           ? &((top_dir_ob->folder_string)[1])
                           : top_dir_ob->folder_string) ,
                         top_dir_ob->nbp);

   if (flag = Bool_TRUE)
     AlMsg_set_removed_flag(the_msg, Bool_TRUE);

   AlMHfolder_debug(" Exiting << AlMHfolder_remove_cur_msg()");
   return(flag);

 };                       /* end of AlMHfolder_remove_cur_msg()  */

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

Bool AlMHfolder_add_msg(data_ob, the_msg)
     VOIDP data_ob;
     Msg the_msg;
 {
   FILE *fp;
   AlMHdata *mh_data;
   Bool flag;
   char *buffer;
   int temp_file_descriptor, correct_len, len;
   struct dir_ob *top_dir_ob; /* top dir_ob on push-down stack of dir_ob's */

   AlMHfolder_debug(" Entering >> AlMHfolder_add_msg()");

   mh_data = (AlMHdata *) data_ob;
   assert(data_ob != NULL);
   assert(mh_data->dir_ob_list != NULL);

   top_dir_ob = mh_data->dir_ob_list;

   if (top_dir_ob->folder_string == NULL)
     { AlMHfolder_debug(" Exiting << AlMHfolder_add_msg() : no folder_string");
       return(Bool_FALSE);  /* folder wasn't opened, can't add to it */
     };
   if (strlen(top_dir_ob->folder_string) == 0)
     { AlMHfolder_debug(" Exiting << AlMHfolder_add_msg() : no folder_string");
       return(Bool_FALSE);  /* folder wasn't opened, can't add to it */
     };

   flag = Bool_TRUE;
   AlMHfolder_debug("  --  In AlMHfolder_add_msg(): checkpt 1");

   /* Create a process, sending it the msg to be added. MH puts into folder */
   /* (By having MH do the add ("rcvstore"), we avoid having to write code  */
   /* that would be outdated if MH changes its data storage formats/info.)  */
   /* --------------------------------------------------------------------- */
   fp = AlMHfolder_pipe3("%s +%s -create",
		         AlMH_RCVSTORE_PATH,
                         ((top_dir_ob->folder_string)[0] == '+')
                          ? &((top_dir_ob->folder_string)[1])
                          : top_dir_ob->folder_string);
   AlMHfolder_debug("  --  In AlMHfolder_add_msg(): checkpt 2");
   if (fp == NULL)  /* process failed to open */
     { AlMHfolder_debug(" Exiting << AlMHfolder_add_msg(): process open fail");
       return(Bool_FALSE);
     };
   temp_file_descriptor = fileno(fp);

   AlMHfolder_debug("  --  In AlMHfolder_add_msg(): checkpt 3");

   /* Write out states. */
   /* ----------------- */
   if (AlMsg_get_suppress_states_out(the_msg) == Bool_FALSE)
     { /* Output of state info is not suppressed, so write it out to file. */
       /* ---------------------------------------------------------------- */
       buffer = AlMsg_write_states_to_buffer(the_msg);
       if (buffer != NULL)
	 { correct_len = strlen(buffer);
	   if (correct_len != 0)
	     len = write(temp_file_descriptor, buffer, correct_len);
	   else
	     len = 0;
	   if (len != correct_len)
	     { fclose(fp);
	       AlMHfolder_debug(
                    " Exiting << AlMHfolder_add_msg(): states failed");
               Memory_free((VOIDP) buffer);
	       return(Bool_FALSE);
	     };
	   /* Free the space just allocated for the state buffer */
	   Memory_free((VOIDP) buffer);
	 };    /* else no state info to write out */
     };
   AlMHfolder_debug("  --  In AlMHfolder_add_msg(): finished writing states");

   /* Write out User Properties. */
   /* -------------------------- */
   if (AlMsg_get_suppress_uprops_out(the_msg) == Bool_FALSE)
     { /* Output of uprop info is not suppressed, so write it out to file. */
       /* ---------------------------------------------------------------- */
       buffer = AlMsg_write_uprops_to_buffer(the_msg);
       if (buffer != NULL)
	 { correct_len = strlen(buffer);
	   if (correct_len != 0)
	     len = write(temp_file_descriptor, buffer, correct_len);
	   else
	     len = 0;
	   if (len != correct_len)
	     { fclose(fp);
	       AlMHfolder_debug(
                     " Exiting << AlMHfolder_add_msg(): uprops failed");
               Memory_free((VOIDP) buffer);
	       return(Bool_FALSE);
	     };
	   /* Free the space just allocated for the user properties buffer */
	   Memory_free((VOIDP) buffer);
	 };    /* else no uprop info to write out */
     };      /* else msgs were suppressed */

   AlMHfolder_debug("  --  In AlMHfolder_add_msg(): finished writing uprops");

   /* Write out the text of the msg. */
   /* ------------------------------ */
   buffer = AlMsg_get_msg_buffer(the_msg);
   if (buffer != NULL)
     { correct_len = strlen(buffer);
       if (correct_len != 0)
	 len = write(temp_file_descriptor, buffer, correct_len);
       else
	 len = 0;
       if (len != correct_len)
         { fclose(fp);
           AlMHfolder_debug(
		      " Exiting << AlMHfolder_add_msg(): msg text failed");
           return(Bool_FALSE);
         };
     };

   /* Close the pipe, the file has been sent */
   /* -------------------------------------- */
   fflush(fp);
   fclose(fp);

   if (flag == Bool_TRUE)
     AlMHfolder_debug(" Exiting << AlMHfolder_add_msg() : successfully");
   else
     AlMHfolder_debug(" Exiting << AlMHfolder_add_msg() : failing");

   return(flag);
 };                       /* end of AlMHfolder_add_msg()  */

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

NORET AlMHfolder_print_self(data_ob, outfile)
     VOIDP data_ob;
     FILE *outfile;
 {
   AlMHdata *mh_data;
   struct dir_ob *dir_ob_p;

   mh_data = (AlMHdata *) data_ob;
   fprintf(outfile, "---------------------------------\n");
   fprintf(outfile, "MH folder Object (& %d) contains:\n");
   fprintf(outfile, "---------------------------------\n");
   fprintf(outfile, "   file_buffer   = \"%s\"\n", mh_data->file_buffer);
   fprintf(outfile, "   filename      = \"%s\"\n", mh_data->filename);
   if (mh_data->dir_ob_list == NULL)
     fprintf(outfile, "      dir_ob_list is empty (NULL).\n");
   else
     { fprintf(outfile,
	"      List of directories on the stack dir_ob_list are:\n");
       for(dir_ob_p = mh_data->dir_ob_list;
	   dir_ob_p != NULL;
	   dir_ob_p = dir_ob_p->next)
	 { fprintf(outfile,"         ----- dir_ob address = %d ------\n",
		   (int) dir_ob_p);
	   fprintf(outfile,"          name_buffer = \"%s\"\n",
		   dir_ob_p->name_buffer);
	   fprintf(outfile,"          nbp = \"%s\"\n", dir_ob_p->nbp);
	   fprintf(outfile,"          bufsize = %d\n", dir_ob_p->bufsize);
           fprintf(outfile, "         folder_string = \"%s\"\n",
		   dir_ob_p->folder_string);
	   fprintf(outfile,"          fd = %d\n", dir_ob_p->fd);
	   fprintf(outfile,"         -------------------------------------\n");
	 };
     };
 };              /* end of AlMHfolder_print_self()  */

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

NORET AlMHfolder_destroy(data_ob)
     VOIDP data_ob;
 {   
   AlMHdata *mh_data;
   struct dir_ob *dir_ob, *temp_dir_ob;

   /* Destroy the mh data object and any director objects it may have. */
   /* ---------------------------------------------------------------- */
   mh_data = (AlMHdata *) data_ob;
   if (mh_data != NULL)
     { if (mh_data->file_buffer != NULL)
         Memory_free((VOIDP) mh_data->file_buffer);
/*     if (mh_data->filename != NULL)
         Memory_free((VOIDP) mh_data->filename);  */

       /* Destroy any remaining directory objects. */
       /* ---------------------------------------- */
       for (dir_ob = mh_data->dir_ob_list;  /* push-down stack of dir_ob */
            dir_ob != NULL;)
         { if (dir_ob->name_buffer != NULL)
	     Memory_free((VOIDP) dir_ob->name_buffer);
           if (dir_ob->folder_string != NULL)
	     Memory_free((VOIDP) dir_ob->folder_string);
           if (dir_ob->pathname != NULL)
	     Memory_free((VOIDP) dir_ob->pathname);
           if (dir_ob->opened == Bool_TRUE)
	     closedir(dir_ob->dirp);
           temp_dir_ob = dir_ob->next;
           Memory_free((VOIDP) dir_ob);
           dir_ob = temp_dir_ob;
         };
       Memory_free((VOIDP) data_ob);
     };
 };                  /* end of AlMHfolder_destroy()  */

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

VOIDP AlMHfolder_create_from_stdin()
{ 
  Al_fatal_error1(
    " ERROR: Folder missing. MH folders may not be defaulted to stdin.%s\n",
                 " Aborting.");
  return(NULL);
};

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

NORET AlMHfolder_init()
 { 
   AlFolderT_register("mh",
		      AlMHfolder_create_from_file,
		      AlMHfolder_create_from_stdin,
		      AlMHfolder_get_next_msg,
		      AlMHfolder_remove_cur_msg,
		      AlMHfolder_add_msg,
		      AlMHfolder_print_self,
		      AlMHfolder_destroy,
                     /* Bool_FALSE, */           /* can't do stdin folders */
                     /* Bool_TRUE,  */           /* can do nexted folders  */
		      (VOIDP) NULL);
 };           /* end of AlMHfolder_init()  */
