#include "al.h"
#include "rm_folderP.h"
#include "darray.h"
#include "RMFBufferP.h"
#include "rap.h"
#include "string.h"
#include "registry.h"

/****************************************************/
/* This file contains the code for the Rmail Folder    */
/* object, used in Athena Information Lens.         */
/* Written Richard A. Pito, 1988.                  */
/****************************************************/

/***************************************************/
/* This parser works but it is abit incorrect because I had to guese at the */
 /* real format of RMAIL files.  The first character in the labels line of a */
 /* message is either 0 if condensed headers have not been made for the file */
 /* or 1 if they have been.  If it is 1 then the next few lines will be the */
 /* original header followed by the CHEADER_INTRODUCER followed by the */
 /* condensed headers and then the message.  If it is 0 then, according to */
 /* <rlk@Think.COM> the next line should be the CHEADER_INTRODUCER and then */
 /* the real header and real body will follow.  In fact, what does happen is */
 /* thath if it is 0 the real message follows with no CHEADER_INTRODUCER */
 /* interveinig.  As I observe the system right now this code will work.  I */
 /* will admit that the code is a bit conterintuitive because I wrote it */
 /* using information derrived from reverse engineering.  */
/***************************************************/
#define BUFF_CHUNKS 512L
#define LINE_CHUNKS 1024L

#define WAITING_FOR_START 1
#define GETTING_SUL       2         /* labels, user properties, and states */
#define GETTING_HEADER    3  /* the Header*/
#define GETTING_C_HEADER  4  /* the condensed header */
#define GETTING_BODY      5
#define DONE              6

#define BEFORE 1
#define AFTER  2

#define CHEADER_INTRODUCER "*** EOOH ***\n"

static const char * BABBLE_HEADER=
"BABYL OPTIONS:\n\
Version: 5\n\
Labels: \n\
Note:   This is the header of an rmail file.\n\
Note:   If you are seeing it in rmail,\n\
Note:    it means the file has no messages in it.\n";

/*=========== Global Variables to this FILE =========*/ 
Registry RMFIDs;


/*--------------------------------------------------*/
NORET _all_sups(RMF)
RMFolder RMF;
{
  int i,l,j,k,w;
  RmMsg rms;
  l=Darray_len(RMF->Msgs);
  for (i=0;i<l;i++) {
    rms=(RmMsg)Darray_get(RMF->Msgs,i);
    k=Darray_len(rms->StatesUPsAndLabels);
    w=k;
    for (j=0;j<k;j++) 
      printf("%d--%d: %s.\n",i,j,Darray_get(rms->StatesUPsAndLabels,j));
  }
}
/*--------------------------------------------------*/
NORET _all_supsd(d)
Darray d;
{
  int i,l,j,k,w;
  RmMsg rms;
  l=Darray_len(d);
  for (i=0;i<l;i++) {
    printf("%d--: %s.\n",i,Darray_get(d,i));
  }
}

/* the header and the body will comprise the body.  We skip the */
 /* condensed header. */
/*--------------------------------------------------*/
VOIDP AlRMFolder_create_from_file(filename,open_status)
     char *filename;
     int open_status;
{
  RMFolder RMF,OtherRMF;
  register FILE *fp;
  unsigned i;
  ino_t innode;

  assert(filename);             /* this might be innapropriate */
  RMF = (struct RMFolder_str *) Memory_allocate(sizeof(struct RMFolder_str));
  RMF->opened=Bool_FALSE;
  RMF->fp=NULL;
  RMF->fileHeader=NULL;
  RMF->LastMsgN=(-1);
  RMF->FromStdin=Bool_FALSE;
  RMF->Msgs=Darray_create();
  RMF->OpenStatus=open_status;
  RMF->read_name=strcpy((char *) Memory_allocate(strlen(filename)+1),
			filename);
  innode=Get_inumber(filename);
  RMF->INumber=innode;
  if ((OtherRMF=RMF_establish_uniqueness(RMF,innode))==NULL) { /* is unique */
    RMF->RMFid=1;
    RMF->LinksIn=1;
    RMF->OriginalRMF=NULL;

       /* ******************** NOT OPENED ************************ */
    if (open_status==FOLDER_OPEN_RW) {  
         /* *** opening for READ WRITE ************* */
      RMFolder_debug1("Create from file: %s: RW: parsing new one\n", 
		      RMF->read_name);
      if ((fp=fopen(filename,"r"))==NULL) { 
	RMFolder_debug("folder does not exist yet\n");
	RMF=rmf_open_file(RMF,filename);
	RMF->fileHeader=strcpy(Memory_allocate(strlen(BABBLE_HEADER)+1),
			       BABBLE_HEADER);
	return((VOIDP)RMF);
      }
      else {   /* already exists */
	FILE *fp2;
	RMFolder_debug("folder does exist\n");
	if ((fp2=freopen(filename,"r+",fp))==NULL) { 
	  Al_warning1((const char *)"Rmail folder create: could not open file: %s forreading and writing \n",
		      (const char *)filename);
	  Memory_free(RMF); Memory_free(RMF->read_name);
	  return(NULL);
	}
	RMF->opened=Bool_TRUE;
	RMF->fp=fp2;
	RMF->FromStdin=Bool_FALSE;
	RMF->OpenStatus=FOLDER_OPEN_RW;
	RMF->ModTime=OSinter_get_file_mod_time((const char*)RMF->read_name);
	return(Parse_from_stream(RMF));
      }
    }
    else { 
         /* *** opening for APPENDING only ************* */
      RMFolder_debug1("Create from file: %s: APPENDING: parsing new one\n", 
		      RMF->read_name);
      if ((fp=fopen(filename,"r"))==NULL) { 
	RMFolder_debug("APP: folder does not exist yet\n");
	RMF=rmf_open_file(RMF,filename);
	fputs(BABBLE_HEADER,RMF->fp);
	fputc(SEPERATOR,RMF->fp);
	return((VOIDP)RMF);
      }
      else {   /* already exists */
	FILE *fp2;
	RMFolder_debug("APP: folder does exist\n");
	if ((fp2=freopen(filename,"a",fp))==NULL) { 
	  Al_warning1((const char *)"Rmail folder create: could not open file: %s forreading and writing \n",
		      (const char *)filename);
	  Memory_free(RMF); Memory_free(RMF->read_name);
	  return(NULL);
	}
	RMF->opened=Bool_TRUE;
	RMF->fp=fp2;
	RMF->FromStdin=Bool_FALSE;
	RMF->ModTime=(ino_t)OSinter_get_file_mod_time((const char*)RMF->read_name);
	return((VOIDP)RMF);
      }
    }
  }
  else {  
       /* ******************** ALREADY IS OPENED ************************ */
    if (OtherRMF->OpenStatus==FOLDER_OPEN_RW) {  /* we can handle this */
      RMFolder_debug("AOLD: other=RW\n");
    }
    else {
      RMFolder_debug1("AOLD: other=APPEND other name=%s\n",OtherRMF->read_name);
      Al_fatal_error("RMFolder: the rmail folder is not able to read from or write to a file origianlly opened for appending to.  Sorry.  Please contact the Argus group for a fix to this problem.\n");
    }
    RMF->RMFid=OtherRMF->RMFid+1;  /* !!! THIS IS JUST A BIT DANGEROUS.  WE */
				   /* ARE NOT CERTAIN THAT JUST INCREASING */
				   /* AN INNODE NUMBER BY ONE IS GOING TO */
				   /* MAKE SOMETHING UNIQUE !!!!! */
    RMF->LinksIn=1;
    RMF->Msgs=OtherRMF->Msgs;
    RMF->OriginalRMF=OtherRMF;
    RMF->fileHeader=OtherRMF->fileHeader;
    OtherRMF->LinksIn=OtherRMF->LinksIn+1;
    return ((VOIDP)RMF);
  }
}

/*--------------------------------------------------*/
RMFolder rmf_open_file(RMF,filename)
RMFolder RMF;
char*    filename;
{
  FILE *fp;
  if ((fp=fopen(filename,"w+"))==NULL) {
    Al_warning1((const char *)"Rmail folder create: could not open file: %s for reading \n",
		(const char *)filename);
    Memory_free(RMF); Memory_free(RMF->read_name);
    return(NULL);
  }
  RMF->opened=Bool_TRUE;
  RMF->fp=fp;
  RMF->FromStdin=Bool_FALSE;
  RMF->ModTime=OSinter_get_file_mod_time((const char*)RMF->read_name);
  return(RMF);
}


/*--------------------------------------------------*/
VOIDP AlRMFolder_create_from_stdin()
{
  RMFolder RMF;


  RMF = (struct RMFolder_str *) Memory_allocate(sizeof(struct RMFolder_str));
  RMF->opened=Bool_FALSE;
  RMF->INumber=0;
  RMF->RMFid=1;
  RMF->LinksIn=1;
  RMF->OriginalRMF=NULL;
  RMF->read_name=RapStr_duplicate("stdin");
  RMF->LastMsgN=(-1);
  RMF->opened=Bool_FALSE;  /* we have a stream but WE haven;t opened it */
  RMF->fp=stdin;
  RMF->FromStdin=Bool_TRUE;
  RMFolder_debug("create form stdin.\n");
  return(Parse_from_stream(RMF));
}


/*--------------------------------------------------*/
/* this function will return 0 if there is an error in stating the file */
/*----------------------------------------*/
ino_t Get_inumber(filename)
char *filename;
{
  struct stat Stat;
  if (stat(filename,&Stat)==0)
    return(Stat.st_ino);
  else {
    return(0);
  }
}

/*--------------------------------------------------*/
VOIDP Parse_from_stream(RMF)
RMFolder RMF;
{
  FILE *fp=RMF->fp;
  char c, *Line;
  int cint;
  unsigned LineSize, LineIndex, State;
  Bool Acceptable;
  Darray AllMsgs;
  RmMsg Msgq;
  RMFBuffer HeadBuff;

  AllMsgs=Darray_create();
  Msgq=RmMsg_create();
  Acceptable=Bool_FALSE;

  HeadBuff=RMFBuffer_create("",LINE_CHUNKS);
  Line=NewLine(&LineIndex,&LineSize);
  do   {
    cint=fgetc(fp);
    if (LineIndex+1==LineSize) 
      Line=Memory_reallocate(Line,(LineSize+=LINE_CHUNKS));
    if (cint!=EOF)
      Line[LineIndex++]=(char)cint;
    c=(char)cint;
    if (c=='\n' || cint==EOF) {
      Line[LineIndex]='\0';
      if (Line[0]==(char)SEPERATOR) {
	Acceptable=Bool_TRUE;
	if (Line[1]==(char)FORMFEED)
	  State=GETTING_SUL;  /* there is a message to get */
	else
	  State=DONE;         /* no messages after header */
	break;
      }
      else {
	RMFBuffer_append(HeadBuff,Line);
	KillLine(Line);
	Line=NewLine(&LineIndex,&LineSize);
      }
    }
  }
  while (cint!=EOF);
  KillLine(Line);

  if (Acceptable==Bool_FALSE) {
    Al_warning1((const char *)"Rmail folder create: no acceptable RMAIL type messages in the file %s\n",RMF->read_name);
    return(NULL);
  }
  
  RMFolder_debug("parsing::");
  RMF->fileHeader=RMFBuffer_copy_of_buff(HeadBuff);
  RMFBuffer_destroy(HeadBuff);
  Line=NewLine(&LineIndex,&LineSize);
  if (State!=DONE) {
    do {
      cint=fgetc(fp);
      if (LineIndex+2==LineSize) 
	Line=Memory_reallocate(Line,(LineSize+=LINE_CHUNKS));
      if (cint!=EOF)
	Line[LineIndex++]=(char)cint;
      c=(char)cint;
      if (c=='\n' || cint==EOF)  {
	Line[LineIndex]='\0';
	switch (State) {
	case GETTING_SUL:
	      /* the LINE of labels states and ups is only one line */
	  SULCreate(Line,Msgq); /* will fill the darray */
	  State=GETTING_HEADER;
	  KillLine(Line);
	  Line=NewLine(&LineIndex,&LineSize);
	  break;
	case GETTING_HEADER:
	  {
	    unsigned index;
	    index=0;
	    /* technically the seperator should just be a single \n but */
	    /* we should be nice.  This niceness though might cause */
	    /* problems, this should be investigated a bit */
	    AppendToHeader(Msgq,Line);
	    while (Line[index++]==' ');
	    if (Line[index-1]=='\n') {  /* is a blank line and is time to */
	      /* move on to condensed header */
	      State=GETTING_C_HEADER;
	    }
	    KillLine(Line);
	    Line=NewLine(&LineIndex,&LineSize);
	  }
	  break;
	case GETTING_C_HEADER: /* getting condensed header */
	  {
	    unsigned index;
	    static Bool FirstLine=Bool_TRUE; /* This variable is true */
	    /* when we are looking at */
	    /* the first line of the */
	    /* cheader, we need this to */
	    /* make sure the first line */
	    /* is the cheader */
	    /* introducer */
	    index=0;
	    if (FirstLine==Bool_TRUE)  {
	      if (strcmp(Line,CHEADER_INTRODUCER)!=0)  { /* no c header */
		State=GETTING_BODY;
		FirstLine=Bool_TRUE;
		goto GET_BODY;
	      }
	      else
		FirstLine=Bool_FALSE;
	    }
	    /* technically the seperator should just be a single \n but */
	    /* we should be nice.  This niceness though might cause */
	    /* problems, this should be investigated a bit */
	    AppendToCHeader(Msgq,Line);
	    while (Line[index++]==' ');
	    if (Line[index-1]=='\n') {  /* is a blank line and is time to */
	      /* move on to the body of the msg */
	      State=GETTING_BODY;
	      FirstLine=Bool_TRUE;  /* get set for next c header */
	    }
	    KillLine(Line);
	    Line=NewLine(&LineIndex,&LineSize);
	  }
	  break;
	case GETTING_BODY:
	GET_BODY:
	  if (Line[0]==(char)SEPERATOR) { /* at end of msg */
	    Msgq->OwnersRMFid=RMF->RMFid;
	    Darray_addh(AllMsgs,Msgq);
#if (RMF_DEBUG==1)
	    putchar('.');
	    fflush(stdout); 
#endif
	    if (Line[1]==(char)FORMFEED) {
	      Msgq=RmMsg_create();
	      State=GETTING_SUL;   /* get another message */
	    }
	    else
	      State=DONE;
	  }
	  else
	    AppendToBody(Msgq,Line);
	  KillLine(Line);
	  Line=NewLine(&LineIndex,&LineSize);
	  break;
	}
      }
    }
    while (cint!=EOF && State!=DONE);
       /* see the description of the RMAIL format in rm_folderP.h.
	  Perhaps I should check here if there is a valid message 
	  following this opening indicator that this file could be in 
	  the RMAIL fornmat */
  }
  RMF->opened=Bool_TRUE;
  RMF->fp=fp;
  RMF->Msgs=AllMsgs;
  RMFolder_debug("::done\n");
  return((VOIDP) RMF);
}                 

/*--------------------------------------------------*/
char * NewLine(index,size)
unsigned *index;
unsigned *size;
{
  char *Line;
  Line=(char *)Memory_allocate(LINE_CHUNKS);
  if (Line==NULL)
    Al_fatal_error("rmail folder: newline: could not get mem\n");
  *size=LINE_CHUNKS;
  *index=0;
  return(Line);
}

/*--------------------------------------------------*/
NORET KillLine(line)
char *line;
{
  Memory_free(line);
}

#define WORD_CHUNKS 20
/*--------------------------------------------------*/
/* This function created a darray of strings. each string is either a */
 /* folder specific state, a alens user property, or an alens state. */
/*--------------------------------------------------*/
NORET SULCreateOld(line,SUL)
char *line;
Darray SUL;
{
  unsigned LineIndex, WordIndex, WordSize;
  char *word, c;
  Bool Started;

  LineIndex=2;  /* skip over the initial "1," or "0," */
  WordIndex=0;
  word=Memory_allocate(WORD_CHUNKS);
  WordSize=WORD_CHUNKS;
  SUL=Darray_create();
  Started=Bool_FALSE;
  while ((c=line[LineIndex++])!='\n' && c!='\0') {
    if (c==',') {
      word[WordIndex]='\0';
      if (WordIndex!=0) {
	Darray_addh(SUL,word);
	RMFolder_debug1("adding SUP=%s\n",word);
	WordIndex=0;
	word=Memory_allocate(WORD_CHUNKS);
	WordSize=WORD_CHUNKS;
	Started=Bool_FALSE;
      }
    }
    else {  /* get rid of leading blanks */
      if (Started==Bool_FALSE && c==' ') ;
      else {
	Started=Bool_TRUE;
	if (WordIndex+3==WordSize) 
	  word=Memory_reallocate(word,(WordSize+=WORD_CHUNKS));
	word[WordIndex++]=c;
      }
    }
  }
  Memory_free(word);
}

/*----------------------------------------*/ 
NORET List_parse_list_into_darray(d,s)
Darray d;
char* s;
{
  unsigned low, high;
  char c;

  low=0;
  while (isspace(s[low]) && s[low]!='\0') low++;
  high=low;
  while (s[high]!='\0') {
    low=high;
    while (isspace(s[low]) && s[low]!='\0') low++;
    high=low;
    if (s[low]!='\0') {
      while ((s[high]!=',') && (s[high]!='\0')) high++;
      if (high!=low) {
	char *string;
	c=s[high];
	s[high]='\0';
	string=RapStr_duplicate(&(s[low]));
	string=RapString_decode_string(string);
	Darray_addh(d,string);
	s[high]=c;
      }
    }
    while (s[high]!=',' && s[high]!='\0') high++;
    if (s[high]==',') high++;
  }
}


/*--------------------------------------------------*/
NORET SULCreate(line,rms)
char *line;
RmMsg rms;
{
  Darray SUL=rms->StatesUPsAndLabels;
  Darray FolderString=rms->FolderString;
  RMFBuffer fs=RMFBuffer_create(NULL,20);
  unsigned LineIndex, WordIndex, WordSize;
  char *word, c, *fsbuff;
  Bool Started;
  int i;
  
  RMFolder_debug1("pre parse=%d...",Darray_len(SUL));
  i=1;
  while (!(line[i]==',' && line[i+1]==','))
    RMFBuffer_append_char(fs,line[i++]);
  if (i!=2) {
    fsbuff=RMFBuffer_copy_of_buff(fs);
    RMFolder_debug1("fsbuff=%s\n",fsbuff);
    List_parse_list_into_darray(rms->FolderString,fsbuff);
    RMFBuffer_destroy_chunk(fs,fsbuff);
  }
  RMFBuffer_destroy(fs);
  List_parse_list_into_darray(SUL,&(line[++i]));
  RMFolder_debug1("...%d\n",Darray_len(SUL));
}


/*--------------------------------------------------*/
/* This function assumes line is null terminated */
/*--------------------------------------------------*/
void AppendToHeader(msg,line)
RmMsg msg;
char *line;
{
  unsigned l;
  l=strlen(line);
  if (msg->header==NULL) {
    msg->header=Memory_allocate(l+1);
    msg->header[0]='\0';
    msg->HeaderLength=l+1;
  }
  else {
    msg->HeaderLength=msg->HeaderLength+l+1;
    msg->header=Memory_reallocate(msg->header,msg->HeaderLength);
  }
  strcat(msg->header,line);
}
/*--------------------------------------------------*/
/* This function assumes line is null terminated */
/*--------------------------------------------------*/
void AppendToCHeader(msg,line)
RmMsg msg;
char *line;
{
  unsigned l;
  l=strlen(line);
  if (msg->cHeader==NULL) {
    msg->cHeader=Memory_allocate(l+1);
    msg->cHeader[0]='\0';
    msg->CHeaderLength=l+1;
  }
  else {
    msg->CHeaderLength=msg->CHeaderLength+l+1;
    msg->cHeader=Memory_reallocate(msg->cHeader,msg->CHeaderLength);
  }
  strcat(msg->cHeader,line);
}
/*--------------------------------------------------*/
/* This function assumes line is null terminated */
/*--------------------------------------------------*/
void AppendToBody(msg,line)
RmMsg msg;
char *line;
{
  unsigned l;
  l=strlen(line);
  if (msg->body==NULL) {
    msg->body=Memory_allocate(l+1);
    msg->body[0]='\0';
    msg->BodyLength=l+1;
  }
  else {
    msg->BodyLength=msg->BodyLength+l+1;
    msg->body=Memory_reallocate(msg->body,msg->BodyLength);
  }
  strcat(msg->body,line);
}

/*--------------------------------------------------*/
void AlRMFolder_print_self(rmf_dat)
VOIDP rmf_dat;
{
  RMFolder rmf;
  unsigned mn,j,l,l2;
  RmMsg msg;

  rmf=(RMFolder)rmf_dat;

  RMFolder_debug1("%s",rmf->fileHeader);
  l=Darray_len(rmf->Msgs);
  for(mn=0;mn<l;mn++) {
    msg=(RmMsg)Darray_get(rmf->Msgs,mn);
    l2=Darray_len(msg->StatesUPsAndLabels);
    for (j=0;j<l2;j++)
      printf("%s,",Darray_get(msg->StatesUPsAndLabels,j));
    printf(",\n");
    printf("\n*** HEADER ***\n%s\n*** CONDENSED HEADER ***\n%s\n***\
BODY ***\n%s",msg->header,msg->cHeader,msg->body);
  }
}

/*--------------------------------------------------*/
RmMsg RmMsg_create()
{
  RmMsg r;
  r=(RmMsg)Memory_allocate(sizeof(struct RmMsg_str));
  r->StatesUPsAndLabels=Darray_create();
  r->FolderString=Darray_create();
  r->header=NULL;
  r->HeaderLength=0;
  r->cHeader=NULL;
  r->CHeaderLength=0;
  r->body=NULL;
  r->BodyLength=0;
  r->Deleted=Bool_FALSE;
  return(r);
}

/*--------------------------------------------------*/
NORET RmMsg_destroy(rms)
RmMsg rms;
{
  unsigned i,l;
  RMF_debug_c('z');
  l=Darray_len(rms->StatesUPsAndLabels);
  for (i=0;i<l;i++)
    Memory_free(Darray_get(rms->StatesUPsAndLabels,i));
  Darray_destroy(rms->StatesUPsAndLabels);
  l=Darray_len(rms->FolderString);
  for (i=0;i<l;i++)
    Memory_free(Darray_get(rms->FolderString,i));
  Darray_destroy(rms->FolderString);
  if (rms->header!=NULL)
    Memory_free(rms->header);
  if (rms->body!=NULL)
    Memory_free(rms->body);
  if (rms->cHeader!=NULL)
    Memory_free(rms->cHeader);
  Memory_free(rms);
}

/*--------------------------------------------------*/
NORET AlRMFolder_get_next_msg(RMF_data, msg_buff, state_buff,
			      uprop_buff, fs_state_registry,
			      do_nested_folders, no_external_state_usage,
			      no_external_uprop_usage, prev_msg)
     VOIDP RMF_data;
     char **msg_buff, **state_buff, **uprop_buff;
     Registry fs_state_registry;
     Bool do_nested_folders, no_external_state_usage, no_external_uprop_usage;
     Msg prev_msg;
{
  RMFolder RMF;
  Bool write_flag;
  RmMsg rms;
  unsigned i;
  int l,k,e;
  Darray K,V;

  RMF = (RMFolder) RMF_data;
#if (RMF_DEBUG==1)
  printf("\n?%d.",RMF->LastMsgN);
  fflush(stdout);
#endif
  if (RMF->OpenStatus!=FOLDER_OPEN_RW) {
    Al_fatal_error1("RMAIL folder: %s: attempt to read from folder opened for appending only\n",
		    RMF->read_name);
  }
  if ((RMF->LastMsgN)>=(int)(Darray_len(RMF->Msgs))) {
    RMFolder_debug("no more messages\n");
    Memory_free(*msg_buff);
    Memory_free(*state_buff);
    Memory_free(*uprop_buff);
    *msg_buff=NULL;
    *state_buff=NULL;
    *uprop_buff=NULL;
    return;
  }
  else {  /* there is a next message */
    if (prev_msg != NULL) {
      rms=(RmMsg)Darray_get(RMF->Msgs,(unsigned)RMF->LastMsgN);
      /* 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 ;
      rms->Deleted=AlMsg_get_removed_flag(prev_msg);
    }
    else
      write_flag = Bool_FALSE;    /* No msg to even write out yet. */

    /* Write out any states or uprops that are needed, with last msg used */
    /* ------------------------------------------------------------------ */
    if (prev_msg != NULL) {
        /* we do not have to call AlRMFolder_write_FS_msg_to_rms here */
        /* becuase the folder specific states cannot be altered */
      if ((write_flag == Bool_TRUE)
	  && (rms->Deleted != Bool_TRUE))
	AlRMFolder_write_SUP_msg_to_rms(RMF, prev_msg);
  
    /* First, free any spare memory from last Msg used. */
      Memory_free(*msg_buff);  /* thereis no need to copy the msg buffer */
                               /* !!! please look into this !!! */
      Memory_free(*uprop_buff);
      Memory_free(*state_buff);
      /* clear out and free up the old fs registry.  Nopte thate we fill */
      /* it with real copies of the fs states so that in the future if we */
      /* are allowed to alter fs states via runrules it can be done. */
      K=Darray_create();
      V=Darray_create();
      Registry_fetch_contents(fs_state_registry,K,V);
      l=Darray_len(K);
      for (i=0;i<l;i++) {
	if ((Registry_remove(fs_state_registry,Darray_get(K,i)))==Bool_FALSE)
	  assert(0);
	Memory_free(Darray_get(K,i));
	Memory_free(Darray_get(V,i));
      }
      Darray_destroy(K);
      Darray_destroy(V);
    }

    /* this piece of code makes sure that we are reading a message that */
    /* belongs t o this RMFolder object.  If it was written to this file by */
    /* another RMFolder object we do not want to read it. */
    k=1;
    l=(int)Darray_len(RMF->Msgs);
    do {
      e=RMF->LastMsgN+k;
      if (e>=l) {
	rms=NULL;
	break;
      }
      rms=(RmMsg)Darray_get(RMF->Msgs,e);
      k++;
    }
    while (rms->OwnersRMFid!=RMF->RMFid);


    if (rms!=NULL) {
#if (RMF_DEBUG==1)
      putchar('a');
      fflush(stdout);
#endif
      /* these functions allocate memory that will be destroyed by the */
      /* above Memory_free calls */
      *msg_buff=AlRMFolder_get_next_msg_buff(rms);
      *state_buff=AlRMFolder_get_next_state_buff(rms);
      *uprop_buff=AlRMFolder_get_next_uprop_buff(rms);
      AlRMFolder_get_next_fs_states(RMF,rms,fs_state_registry);
      RMF->LastMsgN=e;
    }
    else {
      *msg_buff=NULL;
      *state_buff=NULL;
      *uprop_buff=NULL;
      return;
    }
    RMFolder_debug("ALL SUPS POST NEXT MESSAGE\n");
  }
}

/*--------------------------------------------------*/
NORET AlRMFolder_write_SUP_msg_to_rms(RMF,msg)
RMFolder RMF;
Msg msg;
{
  RmMsg rms;
  unsigned l,i;

  /* remove and free the old contents of the darray and put the new */
  /* one in its place */
  rms=(RmMsg)Darray_get(RMF->Msgs,(unsigned)RMF->LastMsgN);
  l=Darray_len(rms->StatesUPsAndLabels);
  for (i=0;i<l;i++) 
    Memory_free((char*)Darray_get(rms->StatesUPsAndLabels,i));
  Darray_destroy(rms->StatesUPsAndLabels);
  RMFolder_fill_SUP(rms,msg);
}

/*--------------------------------------------------*/
NORET AlRMFolder_write_FS_msg_to_rms(RMF,msg)
RMFolder RMF;
Msg msg;
{
  RmMsg rms;
  unsigned l,i;

  /* remove and free the old contents of the darray and put the new */
  /* one in its place */
  rms=(RmMsg)Darray_get(RMF->Msgs,(unsigned)RMF->LastMsgN);
  l=Darray_len(rms->FolderString);
  for (i=0;i<l;i++) 
    Memory_free((char*)Darray_get(rms->FolderString,i));
  Darray_destroy(rms->FolderString);
  RMFolder_fill_FS(rms,msg);
}

/*--------------------------------------------------*/
/* The following function will work even though we do not fill the header */
 /* field in the rms because the header is already part of the body of the */
 /* message.  Since we get the body from the msg we just send it right out, */
 /* header and all.  It will get written to the file in the correct format. */
/*----------------------------------------*/
Bool AlRMFolder_add_msg(RMF_dat,msg)
VOIDP RMF_dat;
Msg msg;
{
  RMFolder RMF=(RMFolder)RMF_dat;
  RmMsg rms;
  char *s;
  Bool write_flag;
  rms=RmMsg_create();
  s=AlMsg_get_msg_buffer(msg);
  rms->BodyLength=strlen(s)+1;
  rms->body=strcpy(Memory_allocate(strlen(s)+1),s);
  rms->header=NULL;  
  rms->cHeader=NULL;
  rms->OwnersRMFid=RMF->RMFid;
  RMFolder_fill_FS(rms,msg);
  write_flag = ((AlMsg_get_state_field_count(msg) == 0)
		&& (AlMsg_get_suppress_states_out(msg) == Bool_TRUE)
		&& (AlMsg_get_init_uprop_field_count(msg) == 0)
		&& ((AlMsg_get_suppress_uprops_out(msg) == Bool_TRUE)
		    OR (AlMsg_get_num_perm_uprops(msg) == 0)))
    ? Bool_FALSE
      : Bool_TRUE ;
  if (write_flag==Bool_TRUE)
    RMFolder_fill_SUP(rms,msg);
  Darray_addh(RMF->Msgs,rms);
  return(Bool_TRUE);    /* THIS IS BOGUS !!! */
}

/*--------------------------------------------------*/
RmMsg RMFolder_fill_FS(rms,msg)  
RmMsg rms;
Msg msg;
{
  Registry fsr;
  Darray Keys, Values, NewFS;
  int i,l;
  char *k;

    /* ==== add the folder specific strings ==== */
  NewFS=Darray_create();
  Keys=Darray_create();
  Values=Darray_create();
  fsr=AlMsg_get_fs_state_registry(msg);
  Registry_fetch_contents(fsr,Keys,Values);
  l=Darray_len(Keys);

  for (i=0;i<l;i++) {
    k=Darray_get(Keys,i);
    Darray_addh(NewFS,strcpy(Memory_allocate(strlen(k)+1),k));
  }
  rms->FolderString=NewFS;
  return(rms);
}

/*--------------------------------------------------*/
/* this function fills the StatesUPsAndLabels field of the rms object by */
 /* replacing the old darray woth a new one, it does not delete the old one */
 /* or its contents. */
/*----------------------------------------*/
RmMsg RMFolder_fill_SUP(rms,msg)
RmMsg rms;
Msg msg;
{
  unsigned l,i;
  char *k, *v, *biff, *buffLine;
  RMFBuffer Buff;
  Darray NewSUP;

  NewSUP=Darray_create();


  /* ==== add the SUP strings ==== */
  /* add the user propoerties to the lable line */
  if (AlMsg_get_suppress_uprops_out(msg) == Bool_FALSE) {
    biff=AlMsg_write_uprops_to_buffer(msg);
    Buff=RMFBuffer_create(biff,LINE_CHUNKS);
    while ((buffLine=RMFBuffer_get_next_chunk(Buff,'\n'))!=NULL) {
      Darray_addh(NewSUP,buffLine);
    }
    RMFBuffer_destroy(Buff);
    Memory_free(biff);
  }

  /* add the athena lens states to the labels line */
  if (AlMsg_get_suppress_states_out(msg) == Bool_FALSE) {
    biff=AlMsg_write_states_to_buffer(msg);
    Buff=RMFBuffer_create(biff,LINE_CHUNKS);
    while ((buffLine=RMFBuffer_get_next_chunk(Buff,'\n'))!=NULL) { 
      Darray_addh(NewSUP,buffLine);
    }
    RMFBuffer_destroy(Buff);
    Memory_free(biff);
  }
  rms->StatesUPsAndLabels=NewSUP;
  return(rms);
}

/*--------------------------------------------------
static NORET SpaceCompress(s)
char* s;
{
  int i,j,l;
  i=j=0;
  while (s[j]!='\0') {
    if (s[i]==' ') j++;
    else { i++; j++; }
    s[i]=s[j];
  }
  if (s[i]==' ') s[i]='\0';
  if (i<j && s[i+1]==' ') s[i+1]='\0';
}
      
*/

/*--------------------------------------------------*/
NORET Labels_add(Buff,s)
RMFBuffer Buff;
char *s;
{
  RMFBuffer_append(Buff,s);
  RMFBuffer_append(Buff,",");
}

/*======================================================================*/
/*  THESE NEXT FIVE FUNCTIONS CREATE BUFFERS TO PASS TO THE MSG OBJECT  */
/*======================================================================*/

/*--------------------------------------------------*/
char * AlRMFolder_get_next_msg_buff(rms)
RmMsg rms;
{
  char *s;
  s=(char *)Memory_allocate(rms->HeaderLength+rms->BodyLength+2);
  s=strcpy(s,rms->header);
  s=strcat(s,rms->body);
  return(s);
}

/*--------------------------------------------------*/
char * AlRMFolder_get_next_state_buff(rms)
RmMsg rms;
{
  char *s, *r;
  RMFBuffer Line;
  unsigned i,l;

  Line=RMFBuffer_create((char *)NULL,LINE_CHUNKS);
  l=Darray_len(rms->StatesUPsAndLabels);
  for (i=0;i<l;i++) {
    s=Darray_get(rms->StatesUPsAndLabels,i);
    if (SequentialMatch(s,0,AlMsg_STATE_PREFIX)) {
      RMFBuffer_append(Line,s);
      RMFBuffer_append(Line,"\n");
    }
  }
  r=RMFBuffer_copy_of_buff(Line);  /* very inefficient */
  RMFBuffer_destroy(Line);
  return(r);
}

/*--------------------------------------------------*/
char * AlRMFolder_get_next_uprop_buff(rms)
RmMsg rms;
{
  char *s, *r;
  RMFBuffer Line;
  unsigned i,l;

  Line=RMFBuffer_create((char *)NULL,LINE_CHUNKS);
  l=Darray_len(rms->StatesUPsAndLabels);
  for (i=0;i<l;i++) {
    s=Darray_get(rms->StatesUPsAndLabels,i);
    if (SequentialMatch(s,0,AlMsg_UPROP_PREFIX)) {
      RMFBuffer_append(Line,s);
      RMFBuffer_append(Line,"\n");
    }
  }
  r=RMFBuffer_copy_of_buff(Line);  /* very inefficient */
  RMFBuffer_destroy(Line);
  return(r);
}
/*--------------------------------------------------*/
NORET AlRMFolder_get_next_fs_states(RMF,rms,States)
RMFolder RMF;
RmMsg rms;
Registry States;
{
  int i,l;
  char *key;

  l=Darray_len(rms->FolderString);
  for (i=0;i<l;i++) {
    key=Darray_get(rms->FolderString,i);
    Registry_add(States,
		 (VOIDP)strcpy(Memory_allocate(strlen(key)+1),
			       key),
		 (VOIDP)strcpy(Memory_allocate(2),""));
  }
}

/*--------------------------------------------------*/
Bool SequentialMatch(s,index,pattern)
char *s, *pattern;
unsigned index;
{
  unsigned sI, pI;
  for (sI=index,pI=0;s[sI]!='\0' && pattern[pI]!='\0';sI++,pI++)
    if (s[sI]!=pattern[pI]) break;
  if (pattern[pI]=='\0') return(Bool_TRUE);
  else return(Bool_FALSE);
}


/*--------------------------------------------------*/
NORET AlRMFolder_destroy(RMF_dat)
VOIDP RMF_dat;
{
  RMFolder RMF=(RMFolder)RMF_dat, ORMF;
  unsigned i,j,l,l2;
  RmMsg rms;
  FILE *temp, *shell;
  char *tempName, *s, *command;
  RMFBuffer B;
  OSinter_TimeType CurrModTime;
  Bool overwrite;
  int commas;

  /* here we make sure that if we write out this file we are the last ones */
  /* to do it.  This is accomplished by the LinksIn field in the RMFodler. */
  /* It is greater than 1 if other RMFolders have been using the file.  Only */
  /* the last call to .._destroy will write the file out. */
  if (RMF->OriginalRMF==NULL) {  /* we are the original */
   RMFolder_debug("Destroy:original..."); 
    if (RMF->LinksIn!=1) {
      RMFolder_debug("no destropyed\n");
      return;
    }
    else
      RMFolder_debug("destropyed\n");
  }
  else {  /* we are not the original */
    RMFolder_debug("Destroy: not original..");
    ORMF=RMF->OriginalRMF;
    Memory_free(RMF->read_name);
    Memory_free(RMF);
    if (ORMF->LinksIn!=1)  {
      (ORMF->LinksIn)--;
      RMFolder_debug("not destroyed.\n");
      return;
    }
    else
      RMFolder_debug("destroyed.\n");
  }

  Registry_remove(RMFIDs,(CONSTVOIDP)RMF->INumber);
  if (RMF->OpenStatus==FOLDER_OPEN_APPEND) {
    RMFolder_debug("appending messages to file\n");
    CurrModTime=OSinter_get_file_mod_time((const char*)RMF->read_name);
    if (OSinter_before(RMF->ModTime,CurrModTime)==Bool_TRUE) {
      int input;
      printf("RMAIL folder interface: the RMAIL file %s has been modified\n",
	     RMF->read_name);
      printf("since it was last read in.  Do you wish this application to\n\
still append any additions to the file? (y/n)[n] ");
      input=getchar();
      if ('y'!=(char)input) {
	char *tn;
	printf("File not appended to.\n");
	tn=rmf_create_second_name(RMF->read_name);
	printf("The messages to be appended are being written to %s.\n",
	       tn);
	fclose(RMF->fp);
	Memory_free(RMF->read_name);
	RMF->read_name=tn;
	  if ((RMF->fp=fopen(RMF->read_name,"w"))==NULL)
         Al_fatal_error1("RMFolder: could not open secondary output file %s\n",
			 RMF->read_name);
	fputs(BABBLE_HEADER,RMF->fp);
	fputc(SEPERATOR,RMF->fp);
      }
    }
    rmf_write_messages_to_file(RMF,RMF->fp);
    fclose(RMF->fp);
  }
  else {
    if (RMF->opened==Bool_TRUE)
      fclose(RMF->fp);

    if (RMF->FromStdin!=Bool_TRUE) {
      tempName=CreateTempName(RMF->read_name);
      if ((temp=fopen(tempName,"w"))==NULL) {
	Al_fatal_error1((const char *)"RMAIL: destroy: cannot open temp file %s",tempName); 
      }
    
      fputs(RMF->fileHeader,temp);
      fputc(SEPERATOR,temp);
      rmf_write_messages_to_file(RMF,temp);
      fclose(temp);

      overwrite=Bool_TRUE;
      CurrModTime=OSinter_get_file_mod_time((const char*)RMF->read_name);
      if (OSinter_before(RMF->ModTime,CurrModTime)==Bool_TRUE) {
	int input;
	printf("RMAIL folder interface: the RMAIL file %s has been modified\n",
	       RMF->read_name);
	printf("since it was last read in.  Do you wish this application to\n\
overwrite any changes that were made to the file? (y/n)[n] ");
	input=getchar();
	if ('y'!=(char)input) {
	  printf("File not overwritten.  The file which was to be saved is\n\
now called %s.\n",tempName);
	  overwrite=Bool_FALSE;
	}
      }
      if (overwrite==Bool_TRUE) {
	if (rename(tempName,RMF->read_name)!=0)
	  Al_fatal_error2((const char *)"AlRMFolder_destroy: cannot rename file %s as %s\n",tempName,RMF->read_name);
	Memory_free(tempName);
      }
    }
    else {  /* is from stdin */
      l=Darray_len(RMF->Msgs);
      for (i=0;i<l;i++) {
	rms=(RmMsg)Darray_get(RMF->Msgs,i);
	RmMsg_destroy(rms);
      }
    }
  }
  Memory_free(RMF->read_name);  /* yes, even if it is "stdin" I copied it */
  if (RMF->fileHeader!=NULL)
    Memory_free(RMF->fileHeader);
  Darray_destroy(RMF->Msgs);
  Memory_free(RMF);
}

/*--------------------------------------------------*/
char* rmf_create_second_name(n)
char *n;
{
  int i;
  char *nn;
  i=strlen(n);
  nn=Memory_allocate(i+11);
  strcpy(nn,n);
  strcat(nn,".rmf-back");
  return(nn);
}

/*--------------------------------------------------*/
NORET rmf_write_messages_to_file(RMF,temp)
RMFolder RMF;
FILE *temp;
{
  int l,i,j,l2;
  RmMsg rms;
  char *s, *string;

  l=Darray_len(RMF->Msgs);
  for (i=0;i<l;i++) {
    rms=(RmMsg)Darray_get(RMF->Msgs,i);
          /* output the beginer, line feed, and the status of the condensed */
	  /* header */
    if (rms->Deleted==Bool_FALSE) {
      fputc(FORMFEED,temp);
      fputc('\n',temp);

      if (rms->cHeader==NULL) 
	fputc('0',temp);
      else
	fputc('1',temp);
      
      {
	int k,n;
	char *key;
	n=Darray_len(rms->FolderString);
	for (k=0;k<n;k++) {
	  key=Darray_get(rms->FolderString,k);
	  fputc(',',temp);
	  fputs(key,temp);
	  RMFolder_debug1("Writing folder string=%s\n",key);
	}
      }
      fputs(",,",temp);

      l2=Darray_len(rms->StatesUPsAndLabels);
      for (j=0;j<l2;j++) {
	s=Darray_get(rms->StatesUPsAndLabels,j);
	if (s==NULL) 
	  Al_fatal_error("RMAIL: writing file out: bad SUP,==NULL");  
	  /* encode all characters volatile to the RMAIL folder and all */
	  /* unprintables as well.  */
	string=RapString_encode_string(s,RMAIL_ENCODED_CHARS);
	fputs(string,temp);
	fputc(',',temp);
	Memory_free(string);
      }
      fputc('\n',temp);
	
        /* at first it may seem strange that a flow of the program can end */
	/* up not writing out any headers out but this will only happen when */
	/* the message was added to the RMFolder using AlRMFolder_add_msg(). */
	/* See that function for a description of how the header is hidden */
	/* in the body of the msg */
      if (rms->header!=NULL)
	fputs(rms->header,temp);
      
      if (rms->cHeader!=NULL)
	fputs(rms->cHeader,temp);
      
      fputs(rms->body,temp);
      fputc(SEPERATOR,temp);
    }
    RmMsg_destroy(rms);
  }
}

/*--------------------------------------------------*/
char *CreateTempName(name)
char * name;
{
  char *s;
  s=(char *)Memory_allocate(strlen(name)+12);
  strcpy(s,name);
  strcat(s,".ALENS.TMP");
  return(s);
}

/*--------------------------------------------------*/
/* This function checks to see weather or not 'unseen' is a state of the */
 /* message */
/*----------------------------------------*/
Bool RMF_is_unseen_in_SUP(rms)
RmMsg rms;
{
  unsigned i,l;
  l=Darray_len(rms->StatesUPsAndLabels);
  for (i=0;i<l;i++)
    if (strcmp(Darray_get(rms->StatesUPsAndLabels,i),"unseen")==0)
      return(Bool_TRUE);
  return(Bool_FALSE);
}
	       
/*--------------------------------------------------*/
Bool AlRMFolder_remove_cur_msg(RMF_dat,msg)
VOIDP RMF_dat;
Msg msg;
{
  RMFolder RMF=(RMFolder)RMF_dat;
  RmMsg rms;

  rms=(RmMsg)Darray_get(RMF->Msgs,(unsigned)RMF->LastMsgN);
  rms->Deleted=Bool_TRUE;
  RMFolder_debug("\nremove current msg\n");
  return(Bool_TRUE);
}
/*--------------------------------------------------*/
RMFolder RMF_establish_uniqueness(RMF,innode)
RMFolder RMF;
ino_t innode;
{
  static Bool Inited=Bool_FALSE;
  RMFolder OtherRMF;

  if (Inited==Bool_FALSE) {
    RMFIDs=Registry_create(Registry_ptrcmp,Registry_ptrhash);
    Inited=Bool_TRUE;
  }
  OtherRMF=(RMFolder)Registry_get(RMFIDs,
				  (CONSTVOIDP)innode);
  if (OtherRMF==NULL)
    Registry_add(RMFIDs,(VOIDP)innode,(VOIDP)RMF);
  return(OtherRMF);    /* do not parse */
}

/*--------------------------------------------------*/
NORET AlRMFolder_init()
{
  AlFolderT_register("RMAIL",
		     AlRMFolder_create_from_file,
		     AlRMFolder_create_from_stdin,
		     AlRMFolder_get_next_msg,
		     AlRMFolder_remove_cur_msg,
		     AlRMFolder_add_msg,
		     AlRMFolder_print_self,
		     AlRMFolder_destroy,
                     /* Bool_FALSE, */           /* can't do stdin folders */
                     /* Bool_TRUE,  */           /* can do nexted folders  */
		     (VOIDP) NULL);
}           /* end of AlRMFolder_init()  */
