#include "args.h"
#include "config.h"

/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#ifndef lint
static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/edit.c,v 2.2 90/10/04 18:24:03 melissa Exp $";
#endif

/*
 * edit.c:
 * edit message in mail file, edit outgoing message, ^E action in
 * paragraph parse to edit outgoing message being composed.
 */

/* VERBOSE_MESSAGES is for fdc's code to debug truncated files after */
/* return from EMACS */
/* #define VERBOSE_MESSAGES */

#include "mm.h"
#include "keytab.h"
#include "rd.h"
#include "message.h"

#define EDIT_HEADERS	0x1
#define EDIT_TEXT	0x2
#define EDIT_MESSAGE	0x4

/* prefix for creating temp files */
#ifndef HAVE_FLEXFILENAMES
#define PRE_OUTGOING ".mm-o"
#define PRE_INREPLY  ".mm-i"
#define PRE_HEADERS  ".mm-h"
#define PRE_MESSAGE  ".mm-m"
#define PRE_COMMAND  ".mm-c"
#define PRE_SPELL    ".mm-s"
#else
#define PRE_OUTGOING ".mm-outgoing."
#define PRE_INREPLY  ".mm-in-reply-to."
#define PRE_HEADERS  ".mm-headers."
#define PRE_MESSAGE  ".mm-message."
#define PRE_COMMAND  ".mm-command."
#define PRE_SPELL    ".mm-spell."
#endif

/* type of backup file to remove */
#define GEMACS	1
#define SPELL	2

#define mm_purge(tf) { \
if (tf) { if (!keep_tmp_files) unlink(tf); free((void *)tf); tf=NULL; } }

/* Imported stuff */

static char *spell_text ARGS((char *txt));
static char *write_to_temp ARGS((const char *pref, int usetmp, char *str));
static void clear_edit_tempfiles ARGS((void));
static void do_edit_seqmsg ARGS((int n));
static void edit_sequence ARGS((void));
static void gnuemacs_edit_outgoing ARGS((int what));
static void maybe_remove_backups ARGS((const char *name, int type));
static void other_edit_outgoing ARGS((int what));
static void spell_seq ARGS((int n));

static char *msgfile = NULL;		/* msg being replied to's temp  */
					/* file name */
static char *outfile = NULL;		/* outgoing msg's temp file */
static char *hfile = NULL;		/* header temp file */
static char *cmdfile = NULL;		/* cmd file */


/*
 * CMD_EDIT:
 */

void
#if HAVE_STDC
cmd_edit (int n)
#else /* K&R style */
cmd_edit (n)
int n;
#endif /* HAVE_STDC */
{
  if (mode & MM_SEND) {
    edit_outgoing (TRUE);
  }
  else {
    edit_sequence();
  }
}

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

/*
 * EDIT_SEQUENCE:
 */

static void
edit_sequence (VOID) {
  if (!check_cf (O_RDWR))		/* pre-check file existence */
    return;
  parse_sequence ("current",NULL,NULL);	/* parse the message sequence */
  if (!check_cf (O_WRONLY))		/* check writable after they CR */
    return;
  if (editor == NULL) {
    fprintf (stderr, "\
Cannot edit.  The editor variable is not set.  Use the SET EDITOR command\n\
to set it first.\n");
    return;
  }
  if (gnuemacs_mmail) {
    if (mmail_path[0] == '\0') {
      fprintf (stderr, "\
Cannot edit.  You are attempting to use gnuemacs mmail mode, but the\n\
mmail-path variable is not set.  Use the SET MMAIL-PATH command to set\n\
it first.\n");
      return;
    }
  }
  sequence_loop (do_edit_seqmsg);	/* run run the edit command over it */
}

#ifdef FDC_EDITFIX
/*
 * msleep():
 * millisecond sleep.
 * Returns 0 on success, -1 on failure to sleep.
 * Needs to be generalized to other platforms.
 * fdc Wed Jan  9 15:36:49 2002
 */

int
msleep(m) int m; {
#if MM_OS_BSD || MM_OS_SOLARIS || MM_OS_SVR4 || MM_OS_AIX
    struct timeval tv;
    struct timezone tz;
#endif /* MM_OS_BSD etc */
    if (m <= 0)				/* In case arg < 1 */
      return(0);
    if (m >= 1000) {			/* In case arg > 1000 */
	sleep(m/1000);
	m = m % 1000;
	if (m < 10)
	  return(0);
    }
#if MM_OS_BSD || MM_OS_SOLARIS || MM_OS_SVR4 || MM_OS_AIX
/*
  gettimeofday() and select() are not portable.  Even platforms that have
  both might use different calling conventions (e.g. gettimeofday() with
  one arg rather than two; struct timezone not defined, different data types
  for select() args, etc), so be careful expanding the coverage of this
  code.
*/
    if (gettimeofday(&tv, &tz) < 0)
      return(-1);
    tv.tv_sec = 0;
    tv.tv_usec = m * 1000L;
    if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv) < 0)
      return(-1);
#else
    if (m > 250) sleep(1);
#endif /* MM_OS_BSD etc */
    return(0);
}

int
chk_edit(fname)
char * fname;
{
#if MM_OS_BSD || MM_OS_SOLARIS || MM_OS_SVR4 || MM_OS_AIX

/*
  This attempts to address a bug in which we edit a message in EMACS,
  exit from EMACS, and upon return to MM find the message truncated,
  presumably because Unix has not finished writing it out yet.  Returns:
   -1 on error;
    0 if file fails to settle down after several seconds;
    1 if all seems OK.
  - fdc Wed Jan  9 15:36:49 2002
*/
    int i, x = 0;
    long size = 0L, prev = 0L;
    struct stat statbuf;
#ifdef VERBOSE_MESSAGES
    printf("chk_edit %s...\n",fname ? fname : "(NULL)");
#endif /* VERBOSE_MESSAGES */

    if (!fname)
      return(-1);
    if (stat(fname,&statbuf) < 0)	/* Get initial file size */
      return(-1);
    size = statbuf.st_size;
    if (msleep(500) < 0)		/* Sleep half a second */
      sleep(1);				/* (or a second) */
    for (i = 0; i < 25; i++) {		/* Loop until size settles */
	if (stat(fname,&statbuf) < 0)
	  return(-1);
	prev = size;
	size = statbuf.st_size;
	if (size > 0L && size == prev) {
#ifdef VERBOSE_MESSAGES
	    if (i > 0)
	      printf("%s: %ld %ld (OK)\n",fname,prev,size);
#endif /* VERBOSE_MESSAGES */
	    return(1);
	}
        /* The size can be 0 because of the bug or because the user  */
	/* really did save an empty file; there's no way to know.    */
	/* But there's no point printing the message when size == 0. */
#ifdef VERBOSE_MESSAGES
	if (size > 0L)
	  printf("%s: %ld %ld (BAD)\n",fname,prev,size);
#endif /* VERBOSE_MESSAGES */
	if (msleep(100) < 0) {
	    if (++x > 1)
	      return(-1);
	    sleep(1);
	}
    }
    return(0);
#else /* MM_OS_BSD etc */
    return(1);
#endif /* MM_OS_BSD etc */
}
#endif /* FDC_EDITFIX */

/*
 * DO_EDIT_SEQMSG:
 */

#ifdef FDC_EDITFIX
static char * xx_bufaddr = NULL;
static int xx_buflen = 0;
#endif /* FDC_EDITFIX */

static void
#if HAVE_STDC
do_edit_seqmsg(int n)
#else /* K&R style */
do_edit_seqmsg(n)
int n;
#endif /* HAVE_STDC */
{
  message *m;
  char *fname;
  int fd;
  FILE *fp;
  char **editargv;
  char *newtext = NULL;
  int count, i, x;
  int trouble = 0;

  if (n <= 0)
    return;				/* no initializing or ending */

  m = &cf->msgs[n];			/* the message we are editting */

  if ((fname = write_to_temp (PRE_MESSAGE, FALSE, m->text)) == NULL)
    return;				/* couldn't write to temp file */

  for (count = 0; editor[count] != NULL; count++)
    ;
  if (gnuemacs_mmail) {			/* write out command file */
    cmdfile = mktempfile (PRE_COMMAND, TRUE);
    if ((fd = open(cmdfile, O_WRONLY|O_CREAT|O_TRUNC, 0700)) < 0) {
      fprintf (stderr, "Trouble creating tempfile\n");
      mm_purge(fname);
      mm_purge(cmdfile);
      return;
    }
    else {
      fp = fdopen (fd, "w");
      fprintf (fp, "nil\nnil\nnil\n%s\n", fname);
      fclose (fp);
    }
 
    editargv = (char **) malloc ((count+4)*sizeof (char *));
    for (i = 0; i < count; i++)
      editargv[i] = editor[i];
    editargv[count++] = cmdfile;
    editargv[count++] = "-l";
    editargv[count++] = mmail_path;
    editargv[count++] = 0;
  }
  else {				/* not gnuemacs_mmail */
    editargv = (char **) malloc ((count+2)*sizeof (char *));
    for (i = 0; i < count; i++)
      editargv[i] = editor[i];
    editargv[count++] = fname;
    editargv[count++] = 0;
  }

  if (mm_execute (editor[0], editargv) != 0) {
    fprintf (stderr, "Edit failed\n");
    mm_purge(fname);
    mm_purge (cmdfile);
    free (editargv);
    return;
  }
#ifdef FDC_EDITFIX
  if (chk_edit(fname) < 1) {
      trouble++;
#ifdef VERBOSE_MESSAGES
      fprintf(stderr,
	      "Possible problem with edit [do_edit_seqmsg:chk_edit].\n");
#endif /* VERBOSE_MESSAGES */
  }
#endif /* FDC_EDITFIX */
  mm_purge(cmdfile);
  free (editargv);

  if ((newtext = read_from_temp(fname)) == NULL) {
#ifdef FDC_EDITFIX
      trouble++;
      fprintf(stderr, "Problem with edit [do_edit_seqmsg:read_from_temp].\n");
#else
      mm_purge(fname);
      return;
#endif /* FDC_EDITFIX */
  }
#ifdef FDC_EDITFIX
  if (xx_bufaddr != newtext) {
      trouble++;
      fprintf(stderr, "Text pointer mismatch [do_edit_seqmsg].\n");
  }
  if ((x = (int)strlen(newtext)) != xx_buflen) {
      fprintf(stderr, "Text length mismatch [do_edit_seqmsg]: %d != %d.\n",
	      xx_buflen,
	      x
	      );
  }
#endif /* FDC_EDITFIX */
  if (gnuemacs_mmail)
    maybe_remove_backups (fname, GEMACS);
  if (!trouble) mm_purge(fname);
  if (m->text) free (m->text);
  m->text = newtext;
  m->keywords = free_keylist(m->keywords);
  get_incoming_keywords(cf, m);
  m->size = strlen(m->text);		/* update the message length */
  if (m->hdrsum) {
    free (m->hdrsum);
    m->hdrsum = NULL;			/* cached header may be wrong */
  }
  m->flags |= M_EDITED|M_SEEN;		/* message has been edited */
  (*msg_ops[cf->type].wr_msg) (cf, m, n, 0); /* write it out (mark as dirty) */
}


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

/*
 * EDIT_OUTGOING:
 */

void
#if HAVE_STDC
edit_outgoing (int p)
#else /* K&R style */
edit_outgoing (p)
int p;
#endif /* HAVE_STDC */
{
  static keywrd editkeys[] = {
    { "all",	0,	(keyval) EDIT_HEADERS|EDIT_TEXT|EDIT_MESSAGE },
    { "headers",0,	(keyval) EDIT_HEADERS },
    { "text",	0,	(keyval) EDIT_TEXT },
  };
  static keytab edittab = { (sizeof(editkeys)/sizeof(keywrd)), editkeys };
  static fdb editfdb = { _CMKEY, 0, NULL, (pdat)&edittab, NULL, "all", NULL };
  pval parseval;
  fdb *used;
  int what;

  if (p) {				/* should we parse? */
    parse (&editfdb, &parseval, &used);
    confirm();
    what = parseval._pvint;
  }
  else {
    what = EDIT_HEADERS|EDIT_TEXT|EDIT_MESSAGE;
  }

  if (editor == NULL) {
    fprintf (stderr, "\
Cannot edit.  The editor variable is not set.  Use the SET EDITOR command\n\
to set it first.\n");
    return;
  }

  if (gnuemacs_mmail) {
    if (mmail_path[0] == '\0') {
      fprintf (stderr, "\
Cannot edit.  You are attempting to use gnuemacs mmail mode, but the\n\
mmail-path variable is not set.  Use the SET MMAIL-PATH command to set\n\
it first.\n");
      return;
    }
    gnuemacs_edit_outgoing (what);
    return;
  }
  else
    other_edit_outgoing (what);
}


/*
 * GNUEMACS_EDIT_OUTGOING:
 */

static void
#if HAVE_STDC
gnuemacs_edit_outgoing (int what)
#else /* K&R style */
gnuemacs_edit_outgoing (what)
int what;
#endif /* HAVE_STDC */
{
  mail_msg *outmsg;
/*  headers *h; */
  int fd;
  FILE *fp;
  char **editargv;
  char *newoutmsg = NULL;		/* new outgoing msg */
  char *newheaders = NULL;		/* new headers from temp file */
  message tmessage;
  mail_msg *new_mail_msg = NULL;
  int count, i, x;
  int trouble = 0;

  outmsg = get_outgoing();

  /* always write out the headers */
  /*  h = outmsg->headers; */
  hfile = mktempfile (PRE_HEADERS, FALSE);
  /* open temp file */
  if ((fd = open(hfile, O_WRONLY|O_CREAT|O_TRUNC, 0700)) < 0) {
    fprintf (stderr, "Trouble creating tempfile\n");
    clear_edit_tempfiles();
    return;
  }
  fp = fdopen (fd, "w");		/* get a FILE * for it */
  display_header (fp, outmsg, TRUE, FALSE); /* expand, not in sendmail mode */
  fclose (fp);

  if (what & EDIT_TEXT) {		/* write out outgoing message text */
    if ((outfile = write_to_temp (PRE_OUTGOING, FALSE, outmsg->body))
	== NULL) {
      clear_edit_tempfiles();
      return;
    }
  }

  if ((mode&MM_ANSWER) && (what&EDIT_MESSAGE)) { /* we are replying to a msg */
    if ((msgfile = write_to_temp (PRE_INREPLY, FALSE,
				  cf->msgs[cf->current].text)) == NULL) {
      clear_edit_tempfiles();
      return;
    }
  }

  /* write out command file for gnuemacs mode */
  cmdfile = mktempfile (PRE_COMMAND, TRUE);
  /* open temp file */
  if ((fd = open(cmdfile, O_WRONLY|O_CREAT|O_TRUNC, 0700)) < 0) {
    fprintf (stderr, "Trouble creating tempfile\n");
    clear_edit_tempfiles();
    return;
  }
  else {
    fp = fdopen (fd, "w");
    fprintf (fp, "%s\n", (what & EDIT_TEXT) ? outfile : "nil");
    fprintf (fp, "%s\n",
	     ((mode&MM_ANSWER)&&(what&EDIT_MESSAGE)) ? msgfile : "nil");
    fprintf (fp, "%s\n", (what & EDIT_HEADERS) ? hfile : "nil");
    fprintf (fp, "nil\n");
    fclose (fp);
  }

  for (count = 0; editor[count] != NULL; count++)
    ;
  editargv = (char **) malloc ((count+4)*sizeof(char *));
  for (i = 0; i < count; i++)
    editargv[i] = editor[i];
  editargv[count++] = cmdfile;
  editargv[count++] = (char *)"-l";
  editargv[count++] = mmail_path;
  editargv[count++] = 0;

  if (mm_execute (editor[0], editargv) != 0) {
    fprintf (stderr, "Edit failed\n");
    clear_edit_tempfiles();
    free (editargv);
    return;
  }
  free (editargv);

  if (outfile) {
#ifdef FDC_EDITFIX
      if (chk_edit(outfile) < 1) {
	  trouble++;
	  fprintf(stderr,
	    "Possible problem with edit [gnuemacs_edit_outgoing:chk_edit].\n");
      }
      if ((newoutmsg = read_from_temp(outfile)) == NULL) {
	  trouble++;
	  fprintf(stderr,
	    "Problem with edit [gnuemacs_edit_outgoing:read_from_temp].\n");
      }
      if (xx_bufaddr != newoutmsg) {
	  trouble++;
	  fprintf(stderr, "Text pointer mismatch [gnuemacs_edit_outgoing].\n");
      }
      if ((x = (int)strlen(newoutmsg)) != xx_buflen) {
	  trouble++;
	  fprintf(stderr,
		  "Text length mismatch [gnuemacs_edit_outgoing]: %d != %d.\n",
		  xx_buflen,
		  x
		  );
      } else if (x < 0) {
	  trouble++;
	  fprintf(stderr,"Warning: Empty text [gnuemacs_edit_outgoing]\n");
      }
      if (trouble || keep_tmp_files)
	printf("Temp file kept: %s\n",outfile);
#else  /* FDC_EDITFIX */
      if ((newoutmsg = read_from_temp (outfile)) == NULL) {
	  clear_edit_tempfiles();
	  return;
      }
#endif /* FDC_EDITFIX */
  }
  if ((newheaders = read_from_temp (hfile)) == NULL) {
#ifdef FDC_EDITFIX
      /* We've never seen a problem with headers... */
      fprintf(stderr,"Header file was null...check .mm-* temp files\n");
#else  /* FDC_EDITFIX */
      clear_edit_tempfiles();
#endif /* FDC_EDITFIX */
      return;
  }
  tmessage.text = newheaders;		/* copy into temp message struct */
  tmessage.size = strlen(newheaders);	/* in order to call parse_msg */
  new_mail_msg = parse_msg (&tmessage);	/* parse the msg (header) */
  if (new_mail_msg->to)
      files_to_fcc(new_mail_msg->to->address, new_mail_msg);
  if (new_mail_msg->cc)
      files_to_fcc(new_mail_msg->cc->address, new_mail_msg);
  if (new_mail_msg->bcc)
      files_to_fcc(new_mail_msg->bcc->address, new_mail_msg);
  free (newheaders);

  if (new_mail_msg->body != NULL)
    free(new_mail_msg->body);
  if (newoutmsg)
    new_mail_msg->body = newoutmsg;
  else {
    new_mail_msg->body = (char *) malloc (strlen(outmsg->body)+1);
    strcpy (new_mail_msg->body, outmsg->body);
  }
  set_outgoing (new_mail_msg);
  if (hfile)
      maybe_remove_backups (hfile, GEMACS);
  if (outfile)
      maybe_remove_backups (outfile, GEMACS);
  if (msgfile)
      maybe_remove_backups (msgfile, GEMACS);
#ifdef FDC_EDITFIX
  if (trouble || ((int)strlen(new_mail_msg->body) < 10)) {
      if (!trouble)			/* i.e. message not printed already */
	fprintf(stderr, "Warning: Message was short.\n");
  } else {
      clear_edit_tempfiles();
  }
#else  /* FDC_EDITFIX */
  clear_edit_tempfiles();
#endif /* FDC_EDITFIX */
}

/*
 * CLEAR_EDIT_TEMPFILES:
 * clean up when edit_outgoing fails for some reason.
 */


static void
clear_edit_tempfiles(VOID) {
  mm_purge(hfile);				/* header file */
  mm_purge(outfile);			/* outgoing message file */;
  mm_purge(msgfile);			/* and/or message file */
  mm_purge(cmdfile);			/* gnuemacs cmd file */
}

/* Delete any existing temp files for this session (fdc) */
/* For use upon exit when temp files are kept. */

clean_edit_tempfiles() {
    unlink(mktempfile(PRE_COMMAND, TRUE));
    unlink(mktempfile(PRE_HEADERS, FALSE));
    unlink(mktempfile(PRE_OUTGOING, FALSE));
    unlink(mktempfile(PRE_MESSAGE, FALSE));
    unlink(mktempfile(PRE_INREPLY, FALSE));
    unlink(mktempfile(PRE_SPELL, FALSE));
}

/*
 * OTHER_EDIT_OUTGOING:
 */

#define hfile hfile_		/* avoid warnings about shadowed global */

static void
#if HAVE_STDC
other_edit_outgoing (int what)
#else /* K&R style */
other_edit_outgoing (what)
int what;
#endif /* HAVE_STDC */
{
  char *tfile = NULL, *hfile = NULL;
  mail_msg *outmsg;
  char *newmsg, *headerstring;
  char **editargv;
  int fd;
  FILE *fp;
  /* headers *h; */
  message tmessage;
  mail_msg *new_mail_msg = NULL;
  int count, i;
  int trouble = 0;

  outmsg = get_outgoing();

  if (!(what & EDIT_HEADERS)) {		/* have to save headers */
      /*  h = outmsg->headers; */
    hfile = mktempfile (PRE_HEADERS, FALSE);
    /* open temp file */
    if ((fd = open(hfile, O_WRONLY|O_CREAT|O_TRUNC, 0700)) < 0) {
      fprintf (stderr, "Trouble creating tempfile\n");
      mm_purge(hfile);
      return;
    }
    else {
      fp = fdopen (fd, "w");		/* get a FILE * for it */
      display_header (fp, outmsg, TRUE, FALSE); /* not in sendmail mode */
      fclose (fp);
    }
  }

  tfile = mktempfile (PRE_OUTGOING, FALSE);
  /* open temp file */
  if ((fd = open(tfile, O_WRONLY|O_CREAT|O_TRUNC, 0700)) < 0) {
    fprintf (stderr, "Trouble creating tempfile\n");
    mm_purge(hfile);
    mm_purge(tfile);
    return;
  }
  else {
    fp = fdopen (fd, "w");		/* get a FILE * for it */
    if (what & EDIT_HEADERS) {
      display_header (fp, outmsg, TRUE, FALSE); /* not in sendmail mode */
      if (what & EDIT_TEXT)		/* both HEADERS and TEXT */
	fputc ('\n', fp);		/* then separate them */
    }
    if (what & EDIT_TEXT)
      display_text (fp, outmsg);
    fclose (fp);
  }

  for (count = 0; editor[count] != NULL; count++)
    ;
  editargv = (char **) malloc ((count+2)*sizeof (char *));
  for (i = 0; i < count; i++)
    editargv[i] = editor[i];
  editargv[count++] = tfile;
  editargv[count++] = 0;

  if (mm_execute (editor[0], editargv) != 0) {
    fprintf (stderr, "Edit failed\n");
    mm_purge(hfile);
    mm_purge(tfile);
    free (editargv);
    return;
  }
  free (editargv);

#ifdef FDC_EDITFIX
  if (chk_edit(tfile) < 1) {
      trouble++;
      fprintf(stderr,
	      "Possible problem with edit [other_edit_outgoing:chk_edit].\n");
  }
#endif /* FDC_EDITFIX */
  if ((newmsg = read_from_temp (tfile)) == NULL) {
#ifdef VERBOSE_MESSAGES
      fprintf(stderr, "Message body: [other_edit_outgoing:read_from_temp].\n");
#endif /* VERBOSE_MESSAGES */
      mm_purge(hfile);
#ifndef FDC_EDITFIX
      if (!trouble && !keep_tmp_files)
	mm_purge(tfile);
#endif /* FDC_EDITFIX */
      return;
  }
  if (hfile) {
      if ((headerstring = read_from_temp (hfile)) == NULL) {
#ifdef VERBOSE_MESSAGES
	  fprintf(stderr, "Headers: [other_edit_outgoing:read_from_temp].\n");
#endif /* VERBOSE_MESSAGES */
	  mm_purge(hfile);
	  if (!trouble) mm_purge(tfile);
	  return;
      }
      tmessage.size = strlen(headerstring)+strlen(newmsg);
      tmessage.text = (char *) malloc (tmessage.size+2);
      if (tmessage.text) {
	  sprintf (tmessage.text, "%s\n%s", headerstring, newmsg);
#ifdef VERBOSE_MESSAGES
      } else {
	  fprintf(stderr,"Failed: [other_edit_outgoing:malloc] (hfile)\n");
#endif /* VERBOSE_MESSAGES */
      }
      free (headerstring);
  } else {
      tmessage.size = strlen(newmsg);
      tmessage.text = (char *) malloc (tmessage.size+1);
      if (tmessage.text) {
	  strcpy(tmessage.text, newmsg);
      } else {
	  fprintf(stderr,"Failed: [other_edit_outgoing:malloc] (!hfile)\n");
      }
  }
  if (newmsg) free (newmsg);
  new_mail_msg = parse_msg (&tmessage); /* parse the msg */
  free (tmessage.text);
  if (!(what & EDIT_TEXT)) {
      if (new_mail_msg->body != NULL)
	free(new_mail_msg->body);
      new_mail_msg->body = (char *) malloc (strlen(outmsg->body)+1);
      if (new_mail_msg->body) {
	  strcpy (new_mail_msg->body, outmsg->body);
      } else {
	  fprintf(stderr,"Failed: [other_edit_outgoing:malloc] (body)\n");
      }
  }
  set_outgoing (new_mail_msg);

  mm_purge(hfile);
  if (!trouble) mm_purge(tfile);
}


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

void
#if HAVE_STDC
cmd_save_draft (int n)
#else /* K&R style */
cmd_save_draft (n)
int n;
#endif /* HAVE_STDC */
{
  char *ofile;
  mail_msg *m;
  FILE *fp;

  noise ("in file");
  ofile = parse_output_file ("file name", NULL, false);
  confirm();
  if (access(ofile, F_OK) == 0) {	/* output file exists */
    cmxprintf ("%s exists, ", ofile);
    if (!yesno("overwrite? ", "no"))	/* shall we overwrite? */
      return;
  }
  m = get_outgoing();
  if ((fp = fopen (ofile, "w")) == NULL) {
    cmpemsg ("Trouble creating output file, draft not saved",CM_SDE);
    return;
  }
  display_header (fp, m, TRUE, FALSE);
  fputc ('\n', fp);
  display_text (fp, m);
  fclose (fp);
  free (ofile);
  printf ("Draft saved in %s\n", ofile);
}

void
#if HAVE_STDC
cmd_restore_draft (int n)
#else /* K&R style */
cmd_restore_draft (n)
int n;
#endif /* HAVE_STDC */
{
  char *text;				/* whole the message text */
  char *fname;				/* file name to read */
  mail_msg *msg;			/* the parsed message. */
  message tmessage;

  noise ("from file");
  fname = parse_input_file( nil, nil, false); /* get the filename */
  confirm();
  if ((text = read_from_temp(fname)) == NULL) { /* read the file */
    free(fname);			/* failure, clean up */
    return;
  }
  free(fname);
  tmessage.text = text;
  tmessage.size = strlen(text);
  msg = parse_msg(&tmessage);		/* parse the message. */
  free(text);				/* throw away the buffer */
  set_outgoing(msg);			/* copy in */
  send_mode(get_outgoing());
}


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

/*
 * MKTEMPFILE:
 * create a temp file using the passed prefix and tacking on this
 * process's pid.
 * NOTE: It is assumed that the string representation of a process pid
 * is at most 9 characters long.
 */

char *
#if HAVE_STDC
mktempfile (const char *pref, int usetmp)
#else /* K&R style */
mktempfile (pref, usetmp)
const char *pref;
int usetmp;
#endif /* HAVE_STDC */
{
  char *name;
  const char *dir;

  if (usetmp)
    dir = TMPDIR;
  else
    dir = (temp_directory[0] != '\0') ? temp_directory
                                      : get_default_temp_dir();
  name = (char *) malloc (strlen(dir) + 1 + strlen(pref)+10);
  sprintf (name, "%s/%s%d", dir, pref, PID);
  return (name);
}


/*
 * WRITE_TO_TEMP:
 * open a temp file, place string in file, close temp file, and return
 * the name of the tempfile.
 * Returns the temp filename upon success, NULL otherwise.
 * Note: the filename should be free'd by the caller when done.
 */

static char *
#if HAVE_STDC
write_to_temp(const char *pref, int usetmp, char *str)
#else /* K&R style */
write_to_temp(pref, usetmp, str)
const char *pref;
int usetmp;
char *str;
#endif /* HAVE_STDC */
{
  char *fname;				/* place to create file name */
  int fd;
  int len;

  fname = mktempfile(pref, usetmp);	/* create a temp file */
  /* open temp file */
  if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0700)) < 0) {
    fprintf (stderr, "Trouble creating tempfile\n");
    free (fname);
    fname = NULL;
    return (NULL);
  }
  if (str != NULL) {
      len = strlen(str);
      if (write(fd, str, len) != len) {
	  fprintf (stderr, "Trouble writing to tempfile\n");
	  close (fd);
	  mm_purge(fname);
	  return (NULL);
      }
  }
  close (fd);
  return (fname);
}


/*
 * READ_FROM_TEMP:
 * read temp file into string.
 * Returns pointer to string on success and NULL on failure.
 * Note: Caller should free the returned string.
 */

char *
#if HAVE_STDC
read_from_temp (const char *fname)
#else /* K&R style */
read_from_temp (fname)
const char *fname;
#endif /* HAVE_STDC */
{
    struct stat sbuf;
    int fd;
    char *newtext;

#ifdef COMMENT
/* What's all this then? */

    int x;
    int len, clen, r;
    long size = -1L;

    if ((fd = open(fname, O_RDONLY,0)) < 0) { /* open file for read */
	perror(fname);
	return (NULL);
    }
    errno = 0;
    x = fsync(fd);			/* some truncation problems */
#ifdef VERBOSE_MESSAGES
    if (x)
      printf("read_from_temp %s fsync=%d errno=%d\n",fname,x,errno);
#endif /* VERBOSE_MESSAGES */

    if (stat(fname,&sbuf) >= 0)
      size = sbuf.st_size;

    len = 0;
    clen = 0;
    newtext = (char *) calloc(clen+=BUFSIZ, 1);
    if (!newtext) {
	perror("read_from_temp calloc");
	return(NULL);
    }
    while (1) {
	if (clen == len) {
	    newtext = (char *) realloc(newtext, clen*=2);
	    if (!newtext) {
		perror("read_from_temp realloc A");
		return(NULL);
	    }
	}
	r = read(fd, newtext+len, clen-len);
	if (r == 0) {
	    break;
	} else if (r == -1) {
	    perror("read_from_temp read");
	    free(newtext);
	    return NULL;
	} else {
	    len += r;
	}
    }
    newtext = realloc(newtext, len+2);
    if (!newtext) {
	perror("read_from_temp realloc B");
	return(NULL);
    }
    close (fd);
#ifdef VERBOSE_MESSAGES
    printf("read_from_temp %s %ld %ld (%s)\n",
	   fname, size,len, (size == len) ? "OK" : "BAD");
#endif /* VERBOSE_MESSAGES */
    if (size == len) {			/* Size is OK */
	if (newtext[len - 1] != '\n')	/* Supply final newline if missing */
	  newtext[len++] = '\n';
	newtext[len] = '\0';		/* null terminate the text */
    } else {				/* So temp file will be kept */
	free(newtext);
	newtext = NULL;
    }

#else  /* COMMENT */
#ifdef FDC_EDITFIX
    int x, size;

    if (stat(fname, &sbuf) == -1) {	/* Get file length */
	fprintf(stderr, "Temporary file disappeared: %s\n", fname);
	return(NULL);
    }
    size = sbuf.st_size;
    if ((long)size != sbuf.st_size || size < 0) {
	fprintf(stderr,
		"read_from_temp: File too long to read: %ld\n",
		sbuf.st_size
		);
	return(NULL);
    }
    errno = 0;
    if ((fd = open(fname, O_RDONLY,0)) < 0) { /* Open file for read */
	fprintf(stderr,
		"read_from_tmp: open(%s) failure, errno=%d\n",
		fname,
		errno
		);
	return(NULL);
    }
    errno = 0;
    newtext = (char *) malloc(size+2);
    if (!newtext) {
	fprintf(stderr,
		"read_from_tmp: malloc(%d) failure, errno=%d\n",
		size+2,
		errno
		);
	return(NULL);
    }
    errno = 0;
    x = read(fd, newtext, size);
#ifdef VERBOSE_MESSAGES
    printf("read_from_temp: %s: %ld %ld (%s)\n",
	   fname, size, x,
	   ((size == x) ? ((size == 0) ? "EMPTY" : "OK") : "BAD")
	   );
#endif /* VERBOSE_MESSAGES */
    if (x != size) {
	fprintf(stderr,
		"read_from_tmp: read(%d) = %d, errno=%d\n",
		size,
		x,
		errno);
	close(fd);
	free(newtext);
	return(NULL);
    }
    close(fd);
    if (size > 1) {
	if (newtext[size-1] != '\n') {
	    newtext[size++] = '\n';
#ifdef VERBOSE_MESSAGES
	    fprintf(stderr,
		    "read_from_tmp: add terminator: new size = %d\n",
		    size
		    );
#endif /* VERBOSE_MESSAGES */
	}
    }
    newtext[size] = '\0';		/* NUL-terminate the text */

    x = strlen(newtext);		/* Check text size again */
    if (x != size) {
	fprintf(stderr,
		"read_from_tmp: size = %d, strlen = %d\n",
		size,
		x
		);
	free(newtext);
	return(NULL);
    }
    xx_bufaddr = newtext;		/* For later checking */
    xx_buflen = x;			/* ... */

#else  /* FDC_EDITFIX */
  if (stat(fname, &sbuf) == -1) {	/* get file length */
    fprintf (stderr, "Temporary file disappeared\n");
    return (NULL);
  }
  if ((fd = open(fname, O_RDONLY,0)) < 0) { /* open file for read */
    fprintf (stderr, "Could not open temporary file\n");
    return (NULL);
  }
  newtext = (char *) malloc (sbuf.st_size+2);
  if (read(fd, newtext, sbuf.st_size) != sbuf.st_size) {
    fprintf (stderr, "Could not read temporary file\n");
    close (fd);
    free (newtext);
    return (NULL);
  }
  close (fd);
  if (sbuf.st_size > 1)
      if (newtext[sbuf.st_size-1] != '\n')
	  newtext[sbuf.st_size++] = '\n';
  newtext[sbuf.st_size] = '\0';		/* null terminate the text */

#endif /* FDC_EDITFIX */
#endif /* COMMENT */

  return (newtext);
}

void
#if HAVE_STDC
cmd_spell(int n)
#else /* K&R style */
cmd_spell(n)
int n;
#endif /* HAVE_STDC */
{
    char *ntext, *otext;
    mail_msg *m;

    if (mode & MM_SEND) {
	confirm();
	m = get_outgoing();
	otext = m->body;
	ntext = spell_text(otext);
	if (otext != ntext && otext != NULL)
	  free(otext);
	m->body = ntext;
    }
    else {
	if (!check_cf(O_RDWR))		/* pre-check we have a file */
	    return;
	parse_sequence ("current",NULL,NULL); /* parse the message sequence */
	if (!check_cf(O_WRONLY))	/* make sure we can write it */
	    return;
	sequence_loop (spell_seq);	/* run the spell command over it */
    }
}


static void
#if HAVE_STDC
spell_seq(int n)
#else /* K&R style */
spell_seq(n)
int n;
#endif /* HAVE_STDC */
{
    char *otext, *ntext;
    message *m = &cf->msgs[n];

    if (n <= 0)
	return;				/* no initializing or ending */

    otext = m->text;
    ntext = spell_text(otext);
    if (otext != ntext && otext != NULL)
      free(otext);
    m->text = ntext;
    m->keywords = free_keylist(m->keywords);
    get_incoming_keywords(cf, m);
    m->size = strlen(m->text);		/* update the message length */
    if (m->hdrsum) {
	free (m->hdrsum);
	m->hdrsum = NULL;		/* cached header may be wrong */
    }
    (*msg_ops[cf->type].wr_msg) (cf, m, n, 0);/* write out (mark as dirty) */
}

static char *
#if HAVE_STDC
spell_text(char *txt)
#else /* K&R style */
spell_text(txt)
char *txt;
#endif /* HAVE_STDC */
{
    const char *fname;
    char *ntxt;
    char **spellargv;
    int count, i;

    if (speller == NULL) {
        fprintf (stderr, "\
Cannot run speller.  The speller variable is not set.  Use the SET SPELLER\n\
command to set it first.\n");
	return (txt);
    }

    if ((fname = write_to_temp (PRE_SPELL, FALSE, txt)) == NULL) {
	fprintf(stderr,"Could not write spell file: %s\n", fname);
	return(txt);			/* couldn't write to temp file */
    }

    for (count = 0; speller[count] != NULL; count++)
	;
    spellargv = (char **) malloc ((count+2)*sizeof(char *));
    for (i = 0; i < count; i++)
	spellargv[i] = speller[i];
    spellargv[count++] = (char *)fname;
    spellargv[count++] = 0;

    if (mm_execute (speller[0], spellargv) != 0) {
        fprintf (stderr, "Could not run speller\n");
	mm_purge(fname);
	free (spellargv);
	return(txt);
    }
    free (spellargv);

    ntxt = read_from_temp(fname);
    if (ntxt == nil) {
	fprintf(stderr,"Could not read spell file: %s\n", fname);
	mm_purge(fname);
	return(txt);
    }
    maybe_remove_backups (fname, SPELL);
    mm_purge(fname);
    return(ntxt);
}


/*
 * maybe_remove_backups:
 * remove backup files created by editor, speller, etc.
 * name is the name of the file whose backups we are going to remove.
 * type is one of the following: GEMACS or SPELL.
 */

static void
#if HAVE_STDC
maybe_remove_backups (const char *name, int type)
#else /* K&R style */
maybe_remove_backups (name, type)
const char *name;
int type;
#endif /* HAVE_STDC */
{
    static fdb backfilfdb = { _CMFIL, FIL_OLD|FIL_WLD,
				  NULL, NULL, NULL, NULL, NULL };
    static fdb tfdb = { _CMTXT, 0, NULL, NULL, NULL, NULL, NULL };
    pval p;
    fdb *u;
    char *filename_template;
    int plen;
    int i;

    /* XXX check a variable controlling removal of backups? */

    /* sufficient space */
    filename_template = (char *) malloc (strlen(name)+10);
    if (!filename_template) {
	fprintf(stderr,"maybe_remove_backups: Memory allocation failure\n");
	return;
    }
    switch (type) {
      case GEMACS:
	strcpy (filename_template, name);
	strcat (filename_template, "*~");
	break;
      case SPELL:
	strcpy (filename_template, name);
	strcat (filename_template, ".bak");
	break;
      default:
	return;
    }
    match (filename_template, (int)strlen(filename_template),
	   fdbchn(&backfilfdb,&tfdb,NULL), &p, &u, &plen);
    free (filename_template);
    if (u == &tfdb)			/* did not get filenames */
	return;
    for (i = 0; p._pvfil[i] != NULL; i++)
	unlink(p._pvfil[i]);
}

