[comp.sys.acorn] Source : emf - electronic mail filter

vanaards%t7@uk.ac.man.cs (01/21/91)

  Firstly, Sorry Tiggr ! 


 Source & Manual page for "emf" - Electronic Mail Filter

-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
| | | | | | | | | | | |  C U T   B E L O W  | | | | | | | | | | | | | | | | |
'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'

/*-------------------------------------------------------------------------*
 * Title   : emf.c
 * Purpose : Uncollate an E-Mail 'box' file.
 *           Knows about Lancaster University PDSOFT mail files (option -l)
 *           Assignes a unique name per message (option -u)
 *           Stores output relative to <mail$dir>
 * History : 09 Jan 1991 created version 1.00
 *-------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <kernel.h>

#define PTR(P) (P?P:"NULL")
#define FALSE  0
#define TRUE   (!FALSE)

#define MAX_FILENAME 10
#define MAX_FILES_IN_DIR 62

typedef struct element List;
struct element {
                 char *filename;
                 char *subject;
                 List *next;
               };

/* Global constants */
const char *Version="1.00";

/* Global variables */
int   display=FALSE, extract=FALSE, lancs=FALSE, uniquefn=FALSE;
FILE *opfile=NULL;
List *hd=NULL;
char *root_dir=NULL;

/* Forward declarations of functions */
void process_mailbox(FILE *);
void read_mail_header(FILE *); 
void process_body(FILE *);     

/*************************************************************************
                        MAIN PROGRAM
 *************************************************************************/

void syntax_message(const char *progname)
{
  fprintf(stderr,"useage : %s -[slu?] filename\n",progname);
}

void help_message(const char *progname)
{
  syntax_message(progname);
  fprintf(stderr,"author : S. van Aardt\n");
  fprintf(stderr,"version: %s (%s)\n\n",Version,__DATE__);
  fprintf(stderr,"purpose: separate collatted e-mail messages\n");
  fprintf(stderr,"options: -s  display 'Subject' headings\n");
  fprintf(stderr,"         -l  assume lancaster university pdsoft format\n");
  fprintf(stderr,"         -d  supply alternative mail destination directory\n");
  fprintf(stderr,"         -u  each message given a unique filename\n");
  fprintf(stderr,"         -?  display help message\n");
  fprintf(stderr,"Note   : <mail$dir> = destination directory, otherwise\n");
  fprintf(stderr,"                      defaults to \"$.usr.spool.mail\"\n");
}
/* -------------------- read_sys_var ---------------------------
 * Purpose    : Obtain the value of a system variable.
 * Parameters : pointer to name of system variable to be read.
 * Returns    : pointer to value read.
 */

char *read_sys_var(const char *variable)
{
 _kernel_swi_regs pre={0}, post={0}; /* registers pre & post call */
 _kernel_oserror *errnum;
 char *buffer=calloc(sizeof(char),255);
 pre.r[0]=(int) variable;
 pre.r[1]=(int) buffer;
 pre.r[2]=255;
 pre.r[3]=0;
 pre.r[4]=3;
 errnum=_kernel_swi(0x23, &pre, &post);  /* SWI "OS_ReadVarVal" */
 if (!errnum) {
   return buffer;
 } else
 {
  free(buffer);
  return NULL;
 }
}


int main(int argc,char *argv[])
{
  /* These 3 are defined in getopt.c */
  extern int  optind;
  extern int  getopt(int ,char **, char *);
  extern char *optarg;
  /* local variables */
  int c=0;
  char *filename=NULL;
  FILE *mb=NULL;
  /* d=display subject headings  */ 
  /* l=lancaster MRS format */
  /* u=unique filenaming regardless of subject heading content */
  char *valid_options="d:slu?"; /* colon means option has an argument */

  root_dir=read_sys_var("mail$dir");
  if (!root_dir) {
    root_dir="$.usr.spool.mail"; /* use this if no mail$path */
  }

  /* Parse any options prefixed by '-' */
  while ((c = getopt(argc,argv, valid_options)) != EOF) /* EOF==-1 */
  {
    switch (c) { 
      case 'd' : root_dir=optarg; break;
      case 'l' : lancs=TRUE; extract=TRUE; break;
      case 's' : display=TRUE; break;
      case 'u' : uniquefn=TRUE; extract=TRUE; break;
      case '?' : help_message(argv[0]); exit(0);
      default  : fprintf(stderr,"error  :unknown option '-%c'\n\n",c);
                 syntax_message(argv[0]); 
                 exit(1);
    } 
  }
  /* Now get the filename belonging to the E-MailBox file */
  if (optind<argc) {
    /* There's scope here for multiple filenames */
    filename=argv[optind];
  } else {
    fprintf(stderr,"error  : missing filename argument\n");
    syntax_message(argv[0]);
    exit(1);
  }
  if ((!extract)&&(!display)&&(!lancs)) {
    fprintf(stderr,"warning: no action selected !\n");
    syntax_message(argv[0]);
    exit(0);
  }
  /* Now process the Mailbox */
  mb=fopen(filename,"r");
  if (mb==NULL) {
    fprintf(stderr,"error  : missing mailbox file '%s'\n",filename);
  } else {
    /* Mailbox file opened successfully, therefor process it */
    if (extract) fprintf(stdout,"Output placed into directory %s\n",root_dir);
    process_mailbox(mb);
    fclose(mb);
  }
}


/*------------------------------- fgetl ---------------------------------*/

#define PARTIAL_STRING_SIZE 40

char *fgetl(FILE *file)
{
  typedef struct strelement StrList;
  struct strelement {
              char *str;  /* pointer to string */
              StrList *next; /* next string */
             };

  StrList *hd=NULL,*tl=NULL; /* list element & start of list */
 
  char *string=calloc(PARTIAL_STRING_SIZE+1,sizeof(char));
  int  count,tmp;
  fgets(string,PARTIAL_STRING_SIZE,file);
  if (feof(file) || (strchr(string,'\n'))) return string;
  count=PARTIAL_STRING_SIZE;
  /* create head of list */
  hd=malloc(sizeof(List));
  hd->next=NULL;
  hd->str=string;
  tl=hd;
  while (!feof(file)) {
    tl=tl->next=malloc(sizeof(List));
    string=tl->str=calloc(PARTIAL_STRING_SIZE+1,sizeof(char));
    fgets(string,PARTIAL_STRING_SIZE,file);
    tmp=strlen(string);
    if ((tmp!=PARTIAL_STRING_SIZE)||feof(file)) {
      count+=tmp;
      if (string[tmp-1]=='\n') {
        break;
      }
    } else count+=PARTIAL_STRING_SIZE;
  }
  tl->next=NULL;
  tl=hd;
  string=calloc(count+1,sizeof(char));
  while (tl) {
    StrList *tmp=tl;
    strcat(string,tl->str);
    free(tl->str);
    tl=tl->next;
    free(tmp);
  }
  return string;
}

/*************************************************************************
              CONVERT UNIX FILENAMES TO RISCOS FILENAMES
 *************************************************************************/

/*---------------------------- Reverse ----------------------------------
 * Purpose    : Reverse the character sequence within a string
 * Parameters : Pointer to string
 * Returns    : Pointer to the same string given as an argument.
 */

char *reverse(char *string)
{
  int hd=0,tl=strlen(string);
  char tmp_c;
  /* reverse the name */
  while (--tl>hd) {                   /* work way back from end of string */
    tmp_c=string[hd];
    string[hd++]=string[tl];
    string[tl]=tmp_c;
  }
  return string;
}

/*---------------------------- isvowel ----------------------------------
 * Purpose    : Determines whether a character is an alphabetical vowel
 * Parameters : character (integer form)
 * Returns    : TRUE if vowel, otherwise FALSE.
 */

int isvowel(int c)
{
  int vowel=FALSE;
  switch (c) {
    case 'a': case 'A':             /* if c matches with */
    case 'e': case 'E':             /* any of these function */
    case 'i': case 'I':             /* will return TRUE */
    case 'o': case 'O':
    case 'u': case 'U': vowel=TRUE;
  }
  return vowel;
}

/*---------------------------- squeeze ----------------------------------
 * Purpose    : Remove any space characters from a string
 * Parameters : pointer to string
 * Returns    : pointer to a new string.
 */


char *squeeze(char *ptr)
{
   int  i=0,j=0;
   char *nn=NULL;
   /* squeeze in-place */
   for (i=j=0; ptr[i]!='\0';i++) if (ptr[i]!=' ') ptr[j++]=ptr[i];
   /* and copy into a new string */
   nn=calloc(j+2,sizeof(char)); /* allocate enough memory for the new string */
   strncpy(nn,ptr,j);
   return nn;
}

/*---------------------------- rm_vowels -------------------------------
 * Purpose    : Removes some vowels from a string thus shortening it
 * Parameters : pointer to string
 * Returns    : pointer to a new string.
 */

char *rm_vowels(char *word,int maxlen)
{
  char *new_word=NULL;
  if (word) {
    int len=strlen(word);
    char *cpy=calloc(len+1,sizeof(char));
    strcpy(cpy,word);
    if (len>maxlen) { 
      /* remove every 2nd vowel */
      int i;
      len--;  /* leave last letter */
      for (i=1; i<len; i+=2) {
        while ( (i<len) && (!isvowel(cpy[i])) ) i++; /* skip non-vowels */ 
        if ( (i<len) && (isvowel(cpy[i])) ) cpy[i]=' '; /* overwrite vowels */
      }
    }
    new_word=squeeze(cpy); 
    free(cpy);
  }
  return new_word;
}

/*---------------------------- shorten ----------------------------------
 * Purpose    : Shortens a string to a specified length
 * Parameters : pointer to string
 *              max length of string to be.
 * Returns    : Pointer to new string
 */

char *shorten(char *ptr,int maxlen)
{
  char *new_ptr;
  int l=strlen(ptr);
  if (l>maxlen) {
    /* quick shortening by removing a chunk from the middle */
    int num=l-maxlen; /* number to remove to get length of 10 */
    int i,j=0;
    for (i=(l-num)/2; j<num; j++) ptr[i+j]=' ';
  }
  new_ptr=squeeze(ptr);
  return new_ptr;
}



/*---------------------------- ufn_rofn ----------------------------------
 * Purpose    : Convert UNIX filename to RISC OS filename 
 * Parameters : pointer to UNIX filename string
 * Returns    : pointer to RISC OS filename
 */

char *ufn_rofn(char *unix_name)
{
  typedef struct l List;         /* structure for constituent parts */
  struct l { char *ptr; struct l *last,*next; };

  int  len;
  char *uname=calloc(strlen(unix_name)+1,sizeof(char)),*rname=NULL,*ptr=NULL;
  List *lptr=NULL,*hdptr=NULL,*tlptr=NULL;

  while ((unix_name) && (*unix_name==' ')) unix_name++;
  if ((unix_name) && (*unix_name)) {
    strcpy(uname,unix_name);
    /* look for root directory '/' at beginning of unix filename */
    if (uname[0]=='/') {
      char *tmp=calloc(2,sizeof(char));
      strcpy(tmp,"$");
      lptr=(List *) malloc(sizeof(List));
      tlptr=lptr;         /* pointer to beginning of list */
      lptr->ptr=tmp;      
      lptr->next=NULL;
      lptr->last=hdptr;
      hdptr=lptr;         /* pointer to end of list */
    }
   /*
    *   Break down the name into its directory components - destroys uname
    */
    ptr=strtok(uname,"/");          /* ignores / if at beginning of list */
    while (ptr) {
      char *tmp=calloc(strlen(ptr)+1,sizeof(char));
      strcpy(tmp,ptr);
      lptr=(List *) malloc(sizeof(List));
      if (!tlptr) tlptr=lptr;      /* make pointer to beginning of list */
      if (hdptr) hdptr->next=lptr; /* link previous element onto this */
      lptr->ptr=tmp;
      lptr->next=NULL;   
      lptr->last=hdptr;  /* reverse chained list */
      hdptr=lptr;
      ptr=strtok(NULL,"/");        /* find next '/' */
    }
    /*
     *  Now go through each component looking for a '.' within each.
     */
    lptr=tlptr;                     /* beginning of list */
    while (lptr) {
      char *dp=NULL;                /* dot position */
      dp=strchr(lptr->ptr,'.');
      if (dp) {
        int len=strlen(lptr->ptr);
        /* 
         * special cases
         */
        /* if only 1 dot then refers to the UNIX csd */
        if (len==1) { 
          char *tmp=calloc(2,sizeof(char));
          strcpy(tmp,"@");
          lptr->ptr=tmp;
        } else {
          if (len==2) { 
            char *tmp=calloc(2,sizeof(char)); 
            strcpy(tmp,"^");
            lptr->ptr=tmp;
          } else {
            /* 
             * General case requires reversing of parts around '.'
             */
            List *dlptr=NULL, *dhdptr=NULL, *dtlptr=NULL,*tmp_next=lptr->next;
            char *dptr=NULL,*cpy=calloc(len+1,sizeof(char));

            strcpy(cpy,lptr->ptr);
            reverse(cpy);           /* reverse complete text */
            dptr=strtok(cpy,".");
            while (dptr) {
              char *tmp=calloc(strlen(dptr)+1,sizeof(char));  
              strcpy(tmp,dptr); /* make a copy of part */ 
              dlptr=(List *) malloc(sizeof(List));
              if (!dtlptr) dtlptr=dlptr;  /* remember beginning of this list */
              if (dhdptr) dhdptr->next=dlptr; /* link previous one onto this */
              dlptr->ptr=reverse(tmp); /* and reverse it's text back again */
              dlptr->next=NULL;
              dlptr->last=dhdptr;  /* reverse chaining  */
              dhdptr=dlptr;
              dptr=strtok(NULL,".");
            } /* endwhile */
            if (lptr->last) {
              free(lptr->ptr);       /* free text worked on by above */
              lptr=lptr->last;       /* move back */
              free(lptr->next);      /* free text's list element */
              lptr->next=dtlptr;     /* link */
              dtlptr->last=lptr;
              dlptr->next=tmp_next;
              if (tmp_next) { 
                tmp_next->last=dlptr;
                lptr=tmp_next->last;
              } else lptr=dlptr;
            } else {        /* case when this is the head of the list */
              free(lptr->ptr);
              free(lptr);
              lptr=dtlptr;
              tlptr=lptr;
              lptr->last=NULL;
              dlptr->next=tmp_next;
              if (tmp_next) {
                tmp_next->last=dlptr;
                lptr=tmp_next->last;
              } else lptr=dlptr;
            } /* endif */
          } /* endif */
        } /* endif */
      } /* endif */ 
      lptr=lptr->next; /* move onto next directory component in list */
    } /* endwhile */
    /* 
     *  Check the lengths of each component and adjust to that for RISC OS
     */
    lptr=tlptr;
    len=0;
    while (lptr) {
      char *new_ptr=NULL;
      if (lptr->next) len+=1;  /* for the RISC OS directory separator */
      new_ptr=rm_vowels(lptr->ptr,MAX_FILENAME);
      free(lptr->ptr);
      lptr->ptr=new_ptr;
      new_ptr=shorten(lptr->ptr,MAX_FILENAME);
      free(lptr->ptr);
      lptr->ptr=new_ptr;
      len+=strlen(lptr->ptr);
      lptr=lptr->next;
    }
    /*
     *  Copy linked list into result string
     */
    rname=calloc(len+1,sizeof(char)); /* make suitable string space */
    lptr=tlptr;
    len=0;
    while (lptr) {
      strcat((rname+len),lptr->ptr);
      free(lptr->ptr);     
      len=strlen(rname);
      lptr=lptr->next;
      if (lptr) strcat(rname,"."); /* append RISC OS directory separator */
    }
    /*
     *  Tidy up, by freeing memory used by list
     */
    lptr=tlptr;
    while (lptr) {
      List *tmp;
      tmp=lptr;
      lptr=lptr->next;
      free(tmp);
    }
  } /* endif */
  free(uname);
  return rname;
}


/* -----------------------------  exists  -----------------------------------
 *
 * Purpose    : Test if a filing System Object exists
 * Parameters : filing system name of object to look.
 * Result     : Returns integer : 
 *                                 0 = Object not Found, 
 *                                 1 = File Found, 
 *                                 2 = Directory Found
 * Notes      : The object name may include path details, in which case the
 *              test will be made wrt the current directory.
 */

int exists(char *name)
{
   int result=0;
  _kernel_oserror *swi_err;       /* Error result structure */
  _kernel_swi_regs i={0},o={0};   /* ARM registers pre & post SWI call */
  i.r[0]=0x11;       /* Reads dir. info for a named object, using no path */
  i.r[1]=(int) name; /* pointer to name (which may be wildcarded) */
  swi_err=_kernel_swi(0x08,&i,&o);
  if (swi_err) {
    fprintf(stderr,"error testing for existance of \"%s\"\n",PTR(name));
    fprintf(stderr,"errornum %d : %s\n",swi_err->errnum,swi_err->errmess);
    exit(1);
  }
  else 
    result=o.r[0]; /* 0=Object not Found, 1=file Found, 2=Directory Found */
  return result;
}

/*---------------------------- mk_dir ------------------
 * Purpose    : Make a specified Filing System directory 
 * Parameters : Pointer to directory name
 * Returns    : nothing
 */

void mk_dir(char *name)
{
  _kernel_oserror *swi_err;       /* Error result structure */
  _kernel_swi_regs i={0},o={0};   /* ARM registers pre & post SWI call */
  i.r[0]=0x08;       /* creates a directory */
  i.r[1]=(int) name; /* pointer to directory name */
  i.r[4]=0;          /* default # entries in directory */
  swi_err=_kernel_swi(0x08,&i,&o);
  if (swi_err) {
    fprintf(stderr,"error making directory \"%s\"\n",PTR(name));
    fprintf(stderr,"error %d : %s\n",swi_err->errnum,swi_err->errmess);
    exit(1);
  }
}

/*---------------------------- dir_exists ----------------------------------
 * Purpose    : Determines whether a directory (or path) exists
 * Parameters : Pointer to directory/path to test for existance 
 * Returns    : If the directory/path exists then returns NULL 
 *              else returns the next directory/path that needs to be made
 */

char *dir_exists(char *dname)
{
  char *ptr=NULL;
  int  bad=FALSE;
  char *dir=calloc(strlen(dname)+1,sizeof(char));
  int x=0;
  do {
    ptr=strchr(dname,'.');
    if (ptr) {
      int num=(int) (ptr-dname);
      if (strlen(dir)) strcat(dir,".");
      strncat(dir,dname,num);
      dname=ptr+1;
    } else { 
      if (strlen(dir)) strcat(dir,".");
      strncat(dir,dname,strlen(dname));
    }
    x=exists(dir);
    switch (x) {
      case -1: /* error */
               exit(0);
               break;
      case 0 : /* not found */
               bad=TRUE;
               break;
      case 1 : /* file found */
               fprintf(stderr,"Can't create directory %s\n",dir);
               exit(0);
               break;
      case 2 : /* directory found */
               break;
    }
  } while ((ptr) && (!bad));
  if (!bad) {
    free(dir);
    dir=NULL;
  }
  return dir;
}

/* ------------------------ tokenise fsname ---------------------------------
 * 
 * Purpose    : Break down a given RISCOS filename into its constituent parts
 * Parameters :   *o,        full object name to break down
 *               **fs,       filing system name  (1)
 *               **ds,       drive specification (2)
 *               **dir,      directory path      (3)
 *               **leaf;     leafname            (4)
 * Returns    : nothing, but arguments fs,ds,dir will be written back.
 *              therefore need to call using the address of fs,ds, dir 
 *
 */

void tokenise_fsname(char *old, char **fs,char **ds, char **dir, char **leaf)
{ /*
   *   *fs=NULL,       filing system name 
   *   *ds=NULL,       drive specification 
   *   *dir=NULL,      directory path 
   *   *leaf=NULL;     leafname
   *
   * examples: 1  Textfile
   *           2  $.Documents.Memos
   *           3  BASIC.Games.Adventures
   *           4  %.BCPL
   *
   *           5  -net-$.SystemMesg
   *           6  scsi::SCSIDisc4.$.usr.man.1.sed
   *           7  adfs:%.asm
   */

  if (old) {
    char *o=calloc(strlen(old)+1,sizeof(char)),*ohd=o; 
    char *ptr;
    int num;
    strcpy(o,old);
    ptr=strpbrk(o,":"); /* returns pointer to first occurance of : or - */
    if ((!ptr) && (*ptr=='-')) ptr=o;
    if (ptr) {
      /* Deal with cases 5,6,7 */  
      if (*ptr=='-') { /* extract filing systen name from between the - signs*/
        char *tmp;
        tmp=strchr(ptr,'-');
        if (!tmp) {
          fprintf(stderr,"Program Error : tokenise_fsname\n");
          fprintf(stderr,"bad name format in %s\n",o);
          exit(1);
        }
        if (*fs) free(*fs);
        num=(int) (tmp-ptr);
        *fs=calloc(num+1,sizeof(char));
        strncpy(*fs,ptr,num);    /* filing system name eg -scsi- */
        o=tmp+1;
      } else { /* extract the filing system name before the first : */
        num=(int) (ptr-o);
        if (*fs) free(*fs);
        *fs=calloc(num+1,sizeof(char));
        strncpy(*fs,o,num);      /* filing system name eg scsi:: or scsi: */
        o=++ptr;                 /* skip ':' */
        if (*o==':') { /* :: is followed by the drive specificaton */
          char *tmp;
          ptr++;                 /* skip over ':' */
          tmp=strchr(ptr,'.');   /* find next '.' */
          if (tmp) { 
            num=(int) (tmp-ptr);
            if (*ds) free(*ds);
            *ds=calloc(num+1,sizeof(char));
            strncpy(*ds,ptr,num);
            o=tmp+1;    /* move ahead of . */
          } else {
            /* possibly fs::drivespec */
            num=strlen(ptr);
            if (num) {
              if (*ds) free(*ds);
              *ds=calloc(num+1,sizeof(char)); 
              strncpy(*ds,ptr,num);
              o=NULL;
              free(ohd); 
            } else {
               fprintf(stderr,"Program Error : tokenise_fsname\n");
               fprintf(stderr,"bad name format in %s\n",o);
               exit(1);
            }
          }
        }
      } /* end of : bit */
    }
    if (o) {
      /* Deal with cases 1,2,3,4 */
      /* o points to the directory path and leafname */
      ptr=strrchr(o,'.'); /* return pointer to last occurance of '.' in o */
      if (ptr) {
        num=(int) (ptr-o);
        if (*dir) free(*dir);
        *dir=calloc(num+1,sizeof(char));
        strncpy(*dir,o,num);
        o=ptr+1;     /* what follows last . should be the leafname */
      }
      num=strlen(o);
      if ((num) && (*o)) {
        if (*leaf) free(*leaf); 
        *leaf=calloc(num+1,sizeof(char));
        strcpy(*leaf,o);       /* leafname */ 
      }
    }
    if (o) free(ohd);
  } else {
    fprintf(stderr,"Program Error: tokenise_fsname called with NULL\n");
    exit(1);
  }
}


/*----------------------- unique_filename ----------------------------
 * Purpose    : create a unique name using a partial file name 
 * Parameters : pointer to a filename which must include "$$"
 * Returns    : pointer to a new unique filename.
 */

char *unique_filename(char *dirname) 
{
  int num=0,numlen,flen;
  char *fnnum,*name;
  /* test if dir exists */
  if (dirname) {
    char *edir=dir_exists(dirname);
    while (edir) {
      mk_dir(edir);      /* build directory structure required */
      if (edir) free(edir);
      edir=dir_exists(dirname);
    } /* else directory structure is there */
  }
  while (TRUE) {
    numlen=((num<10)?1:2);
    if (numlen<MAX_FILES_IN_DIR) {
      fnnum=calloc(numlen+1,sizeof(char));
      flen=(dirname?strlen(dirname):0)+numlen+2;
      name=calloc(flen,sizeof(char));
      sprintf(fnnum,"%u",num);
      if (dirname) sprintf(name,"%s.%s",dirname,fnnum);
      else strcat(name,fnnum);
      if (!exists(name)) break;
      num++;
      free(fnnum);
      free(name);
    } else {
      fprintf(stderr,"error: too many unique files");
      if (dirname) fprintf(stderr," in directory %s\n",dirname);
      else fprintf(stderr," in current directory\n");
      exit(1);
    }
  }
  return name;
}


void mk_opfile(char *uname)
{
  char *fs=NULL,*ds=NULL,*dir=NULL,*leaf=NULL;
  char *outfile=ufn_rofn(uname);
  char *filename=calloc(strlen(root_dir)+1+strlen(outfile)+1,sizeof(char));
  sprintf(filename,"%s.%s",root_dir,outfile);
  free(outfile);
  outfile=filename;
  tokenise_fsname(outfile,&fs,&ds,&dir,&leaf);
  /* must check that the directory structure exists */
  if (dir) {
    char *edir=dir_exists(dir);
    while (edir) {
      mk_dir(edir);      /* build directory structure required */
      if (edir) free(edir);
      edir=dir_exists(dir);
    } /* else directory structure is there */
    if (fs) free(fs);
    if (ds) free(ds);
    if (dir) free(dir);
    if (leaf) free(leaf);
  }
  opfile=fopen(outfile,"w");
  if (opfile==NULL) {
    fprintf(stderr,"Couldn't open file %s\n",outfile);
    exit(1);
  }
  free(outfile);
}

/*************************************************************************
                          MAILBOX PROCESSING 
 *************************************************************************/

void read_mail_header(FILE *fp)
{
  static List *tl=NULL;
  char *line=NULL;
  int  found=FALSE;
  /*  "From "     ...
   *  ...        ...
   *  "Subject:" ... Lancs File <filename>:Part xxx/yyy
   *  ...        ...
   *  ...        ...
   *  
   *  ----- body ---
   */
  /* locate beginning of mail message starts at 'From '*/
  while ((!feof(fp))&&(!found)) {
    line=fgetl(fp);
    if (!strncmp(line,"From ",5)) found=TRUE;
    free(line);
  }
  found=FALSE;
  /* locate subject heading - 'Subject:' */
  while (!feof(fp)) {
    line=fgetl(fp);
    if (!strncmp(line,"Subject:",8)) break;
    free(line);
  }
  if (line) {
    if (display) fprintf(stdout,"%s",line);
    if (lancs) {
      char *ptr=(line+9); /* move ahead of 'Subject: ' part of line */
      if (!strncmp(ptr,"File ",5)) {
        char *filename,*eofn;
        int num;
        ptr+=5;
        eofn=strrchr(ptr,':'); /* File <....>: Part n/m */
        num=(int) (eofn-ptr);
        filename=calloc(num+1,sizeof(char));
        strncpy(filename,ptr,num);
        ptr=eofn+7; /* skip over ': Part ' */
        eofn=strchr(ptr,'/'); /* find '/' in "Part xxx/yyy" */
        num=(int) (eofn-ptr);
        eofn=calloc(num+3,sizeof(char));
        eofn[0]='/';
        eofn[1]='\0';
        strncat(eofn,ptr,num);
        ptr=calloc(strlen(filename)+strlen(eofn)+1,sizeof(char));
        strcpy(ptr,filename);
        strcat(ptr,eofn);
        free(filename);
        free(eofn);
        mk_opfile(ptr);
        free(ptr);
      } else {
        fprintf(stdout,"Subject contents not suitable for filename\n");
      }
    } else {
      if (uniquefn) {
         /* also need to keep a key to each file */
         if (!hd) {
           hd=malloc(sizeof(List));
           hd->next=NULL;
           tl=hd;
         } else { 
           tl->next=malloc(sizeof(List));
           tl=tl->next;
           tl->next=NULL;
         }
         tl->subject=calloc(strlen(line)+1,sizeof(char));
         strcpy(tl->subject,line);
         tl->filename=unique_filename(root_dir);
         opfile=fopen(tl->filename,"w");
         if (opfile==NULL) {
           fprintf(stderr,"Couldn't open file %s\n",tl->filename);
           exit(1);
         }
      }
    }
    free(line);
  }
  /* locate end of header - an empty line */
  found=FALSE;
  while ((!feof(fp))&&(!found)) {
    int l,i;
    line=fgetl(fp);
    l=strlen(line);
    if (l) {
       found=TRUE;
       for (i=0; ((i<l)&&(found)); i++) if (!isspace(line[i])) found=FALSE;
    } else found=TRUE;
    free(line);
  }
  /* lancaster files usually have a ---- Part xxx/yyy  before contents */
  if (lancs) {
    found=FALSE;
    while ((!feof(fp))&&(!found)) {
      line=fgetl(fp);
      if (!strncmp(line,"---- Part ",10)) found=TRUE;
      free(line);
    }
  }
}

void process_body(FILE *fp)
{
  char *line;
  int found=FALSE,eomm=FALSE,l,i;
  /* <body>
   * 
   * From ...
   */
  while ((!feof(fp))&&(!found)) {
    line=fgetl(fp);
    l=strlen(line);
    if ((l>5) && (!strncmp(line,"From ",5)) && (eomm)) found=TRUE;
    if (!found) {
      eomm=TRUE;
      if (l) {
        for (i=0; ((i<l)&&(eomm)); i++) if (!isspace(line[i])) eomm=FALSE;
      }
      if ((extract) && (opfile!=NULL)) {
        if (!lancs) fprintf(opfile,"%s",line);
        if (lancs) {
          if (l>17) {
             char *endlancs="---- End of part ";
             int el=strlen(endlancs);
             if (strncmp(line,endlancs,el)) fprintf(opfile,"%s",line);
          } else {
            if ((l) && (line[0]!='\n')) fprintf(opfile,"%s",line);
          }
        }
      }
    }
    free(line);
  }
  if (opfile!=NULL) {
    fclose(opfile);
    opfile=NULL;
  }
  if (found) fseek(fp,(long) (-l),SEEK_CUR);
}

void process_mailbox(FILE *mb)
{
  while (!feof(mb)) {
    read_mail_header(mb);         /* every mail message has a header */
    process_body(mb);    /* and supposidly a body too ! */
  }
  if ((uniquefn) && (hd)) { 
    List *tl=hd;
    char *index=unique_filename(root_dir);
    FILE *iout=fopen(index,"w");
    if (iout==NULL) {
      fprintf(stderr,"Warning : Couldn't open index file '%s'\n",index);
      fprintf(stderr,"          Dumping index\n");
      iout=NULL;
    } else {
      fprintf(stdout,"key to message filenames stored in file '%s'\n",index);
    }
    while (tl) {
      List *tmp=tl;
      fprintf(iout!=NULL?iout:stdout,"Filename : %s\n",tl->filename);
      fprintf(iout!=NULL?iout:stdout,"%s\n",tl->subject);
      free(tl->subject);
      tl=tl->next;
      free(tmp);
    }
    if (iout!=NULL) fclose(iout);
  }
}


-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
| | | | | | | | | | | |  C U T   B E L O W  | | | | | | | | | | | | | | | | |
'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'-'

EMF(S)                       USER COMMAND                           EMF(S)



NAME 

        emf - electronic mail filter        

SYNOPSIS

        emf  -[slu?]  filename

DESCRIPTION

        Exatract individual mail messages from a collated group within
        a single file (mbox). Special extensions provided for mail from
        the PDsoft mail response server at Lancaster University.

        -s display 'subject' headings as encountered.
        
        -l extract & assume lancs pdsoft format, filenames will be
           assigned to each message according to the content of the 
           subject heading. This invariably includes a directory 
           specification such as micros/ibmpc/e130/ which will be
           made if it doesn't already exist.
                                                
        -u extract assuming general mail headers, which are removed.
           Each message is stored in a uniquely named filename. The last
           one created is a key to the others, containing their subject
           banner and filename.

        -d specify destination directory for the messages extracted.
           Alternatively use <mail$dir>, otherwise a default of
           $.usr.spool.mail is assumed.

FILES

        <mail$dir> specifies destination directory for mail messages.
        
AUTHOR

        Steve van Aardt  

        contact:     until June 1991   : vanaards@uk.man.cs.p4
                     otherwise contact : 2 Oakmere, 
                                         Chorley Old Road,
                                         Brindle,
                                         Lancashire, PR6 8RP.
                                         England.

DIAGNOSTICS

        Hopefully most errors have been catered for, should the program
        fail otherwise then that would be a bug and should be noted as
        such.

BUGS

        Expects lancs mail to conform to a particular format along the
        subject line.

        Results unpredictable - probably crashes - if the maximum number
        of files is reached within the destination directory. Therefore
        wise to tidy it up.

        Lancs format usually comes in BOO format, and may be split over
        several files. The use of an editor will be required to strip
        trailing [newline] characters from the end of each part. The
        same applies to other messages which have been split.
+--------------------------------+-----------------------------------------+
|   ()()TEVEN         ()         |                                         |  
|  ()                ()()        |                                         |  
|   ()()   ()  ()AN ()  ()       |                                         |
|      ()   ()()   ()()()()      +-----------------------------------------+
|   ()()     ()   ()      ()ARDT |JANET E-mail : vanaards@uk.ac.man.cs.p4  |
+--------------------------------+-----------------------------------------+