/*
 *  MAKEMSGN.C
 *
 *  Written on 30-Jul-90 by jim nutt.  Changes on 10-Jul-94 by John Dennis.
 *  Released to the public domain.
 *
 *  Routines to create new messages.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "mctype.h"
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifndef UNIX  /* UNIX only uses system(), and that is in stdlib.h */
#include <process.h>
#endif

#ifdef MSDOS
#include <dos.h>
#ifdef PACIFIC
#include <sys.h>
#else
#endif
#endif

#include "addr.h"
#include "nedit.h"
#include "msged.h"
#include "winsys.h"
#include "menu.h"
#include "dialogs.h"
#include "memextra.h"
#include "nshow.h"
#include "areas.h"
#include "readmail.h"
#include "keys.h"
#include "template.h"
#include "main.h"
#include "wrap.h"
#include "screen.h"
#include "userlist.h"
#include "vsevops.h"
#include "dirute.h"
#include "makemsgn.h"
#include "strextra.h"
#include "group.h"
#include "timezone.h"

#ifdef MSDOS
#ifdef USE_CRITICAL
#include "critical.h"
#endif
#if !defined(NODOSSWAP) && !defined(__FLAT__)
#include "spawn.h"
#endif
#endif

#define TEXTLEN 128
#define INPLEN  60

/* prototypes */

static void reply_msg(int type);
static void crosspost(msg * m);
static int externalEditor(msg * m);

extern int savecc;

static msg *EdMsg;              /* message header being edited */
static int scan_base;           /* force a scan of the msgbase */
int do_lookup = FALSE;          /* lookup thru the nodelist? */
static int editRet;

static int sent_msg(void)
{
    int rc;
    rc = ChoiceBox("", "WARNING: This message has already been sent! Continue?", "Yes", "No", NULL);
    return rc == ID_ONE ? 1 : 0;
}

/*
 *  Creates a duplicate of the passed message header, allocating
 *  memory for it in the process.
 */

#define copystr(d, s) { if (s) { d = xstrdup(s); } else { d = NULL; } }

msg *duplicatemsg(msg * from)
{
    msg *to;

    to = xcalloc(1, sizeof *to);

    *to = *from;                /* copy all the bits */

    copystr(to->isfrom, from->isfrom);
    copystr(to->isto, from->isto);
    copystr(to->subj, from->subj);
    copystr(to->to.domain, from->to.domain);
    copystr(to->from.domain, from->from.domain);
    copystr(to->msgid, from->msgid);
    copystr(to->reply, from->reply);
    copystr(to->replyarea, from->replyarea);
    copystr(to->charset_name, from->charset_name);

    to->text = NULL;

    return to;
}

void newmsg(void)
{
    reply_msg(MT_NEW);
}

void reply(void)
{
    reply_msg(MT_REP);
}

void quote(void)
{
    if (message && message->replyarea)
    {
        reply_msg(SW->rotharea);
    }
    else
    {
        reply_msg(SW->rquote);
    }
}

void reply_oarea(void)
{
    reply_msg(SW->rotharea);
}

void followup(void)
{
    reply_msg(SW->rfollow);
}

void replyextra(void)
{
    reply_msg(SW->rextra);
}

static int findArea(char *tag)
{
    int i = 0;
    int areano;

    while (i < SW->groupareas)
    {
        areano = group_getareano(i);
        if (areano >= 0 &&
            !stricmp(arealist[group_getareano(i)].tag, tag))
        {
            break;
        }
        i++;
    }

    if (i == SW->groupareas)
    {
        i = 0;
    }

    return i;
}

static void reply_msg(int type)
{
    ADDRESS tmp;
    msg *hlink;
    msg *m;
    unsigned long t = CurArea.current;
    unsigned long tl = CurArea.lastread;
    unsigned long link = t;
    int oarea = SW->grouparea;
    int ogroup = SW->group;
    int q = 0;
    msg *oldmsg;                /* contains all the old information */
    unsigned long ulink;
    int toarea = 0, i;
    int count;                  /* for robot names */

    scan_base = 0;
    oldmsg = NULL;

    if (type & MT_NEW)
    {
        RefreshMsg(NULL, 6);
    }

    if ((!(type & MT_NEW) && CurArea.messages == 0) || !CurArea.status)
    {
        return;
    }

    group_set_group(0); /* allow crossposts etc. pp. into all areas, and not
                           only into areas of current group */

    if (type & MT_ARC)
    {
        toarea = SW->grouparea;
        if (message && message->replyarea)
        {
            toarea = findArea(message->replyarea);
        }
        toarea = selectarea("Answer In Area", toarea);
        if (msgederr)
        {
            /* This code at this place is COMPLETE bullshit. No message has
               ever been written, and no prior call to set_area has
               taken place! It should be removed, but first I want to study if
               there are any side effects of it's removal.

            if ((type & MT_ARC) || scan_base || CurArea.msgtype == SQUISH)
            {
                set_area(oarea);
            }
            else
            {
                CurArea.current = link;
                CurArea.messages++;
                CurArea.last++;
            }

            */

            group_set_group(ogroup);

            return;
        }
    }

    if (message)
    {
        oldmsg = duplicatemsg(message);
    }

    if (CurArea.messages == 0 || message == NULL)
    {
        message = xcalloc(1, sizeof *message);
    }

    ulink = message->msgnum;

    /*
     *  We work with m, not message - the set_area() function kills
     *  message automatically.
     */

    m = message;
    message = NULL;

    if (type & MT_REP)
    {
        m->text = clearbuffer(m->text);
    }

    if (type & MT_NEW)
    {
        clearmsg(m);
    }

    if (type & MT_FOL)
    {
        release(m->msgid);
        m->msgid = m->reply;
        m->reply = NULL;
    }

    release(m->reply);

    if (m->msgid)
    {
        m->reply = m->msgid;
        m->msgid = NULL;
    }
    if (type & MT_FOL)
    {
        release(m->isfrom);
        tmp = m->to;
    }
    else
    {
        if (!(type & MT_NEW))
        {
            release(m->isto);
            m->isto = m->isfrom;
        }
        else
        {
            m->to.zone = CurArea.addr.zone;
        }
        tmp = m->from;
    }

    m->isfrom = xstrdup(ST->username);

    if (type & MT_ARC)
    {
        set_area(toarea);
        if (!CurArea.status)
        {
            dispose(oldmsg);
            dispose(m);
            group_set_group(ogroup);
            set_area(oarea);
            return;
        }
    }

    /* TE: Only do lookup when composing a new message. Don't lookup
           when replying to an exisiting message, because we suppose that the
           sender wants the answer to the AKA that he is using. */
    do_lookup = (CurArea.netmail && (type & MT_NEW)) ? TRUE : FALSE;
    m->to = tmp;
    m->timestamp = time(NULL);
    if (SW->tzutc)
    {
        m->timezone = tz_my_offset();
        m->has_timezone = 1;
    }
    m->replyto = 0;
    m->new = 1;
    m->cost = 0;
    m->times_read = 0;
    m->scanned = 0;
    m->soteot = 0;
    m->time_arvd = 0;

    if (CurArea.echomail && CurArea.messages == 0)
    {
        m->msgnum = 2;
    }
    else
    {
        m->msgnum = MsgnToUid(CurArea.messages) + 1;
    }

    /* find a origin aka matching the destination zone for netmails */

    m->from = CurArea.addr;
    if (CurArea.addr.domain)
    {
        m->from.domain = xstrdup(CurArea.addr.domain);
    }

    if ((CurArea.netmail) && (!(type & MT_NEW)))
    {
        akamatch(&(m->from), &(m->to));
    }

    if (m->to.internet || m->to.bangpath)
    {
       char *oldptr =  m->to.domain;
       m->to.domain = compose_internet_address(m->to.domain, m->isto);
       release(oldptr);
       release(m->isto);
    }

    if (!(type & MT_NEW) && !(type & MT_ARC))
    {
        m->replyto = link;
    }
    else
    {
        m->replyto = 0;
    }

    clear_attributes(&m->attrib);
    memset(m->replies, 0, sizeof(m->replies));

    while (!q)
    {
        if (EditHeader(m) == Key_Esc)
        {
            if (confirm("Cancel?"))
            {
                dispose(oldmsg);

                group_set_group(ogroup);
                dispose(m);

                if (type & MT_ARC)
                    set_area(oarea);

                if (CurArea.status)
                {
                    CurArea.current = (t) ? t : CurArea.current;
                    CurArea.lastread = (tl) ? tl : CurArea.lastread;
                }
                return;
            }
        }
        else
        {
            q = 1;
        }
    }

    /*
     *  Create the template for the message.  Will not include template
     *  if being sent to robotnames.  Modified by Kieran Haughey, Andrew
     *  Clarke.
     */

    if (user_list[0].robotname == NULL)
    {
        user_list[0].robotname = "AreaFix";  /* default */
    }
    count = 0;
    for (i = 0; i < MAXUSERS; i++)
    {
        if (user_list[i].robotname == NULL || m->isto == NULL)
        {
            break;
        }
        if (stricmp(user_list[i].robotname, m->isto) == 0)
        {
            count++;
        }
    }

    if (!(type & MT_NEW))
    {
        RefreshMsg(NULL, 6);
    }

    if (count == 0)
    {
        MakeTemplateMsg(m, oldmsg, group_getareano(oarea), type | MT_NEW);
    }

    if (ST->editorName != NULL)
    {
        editRet = externalEditor(m);
    }
    else
    {
        editRet = editmsg(m, ((type & MT_NEW) || (type & MT_REP)) ? 0 : 1);
    }

    switch (editRet)
    {
    case SAVE:
        save(m);
        if (!(type & MT_ARC) && !(type & MT_NEW))
        {
            link = UidToMsgn(ulink);
            if (CurArea.msgtype != QUICK)
            {
                /* somewhere is leaking so i turned it off for QBBS */

                if ((hlink = MsgReadHeader(link, RD_HEADER)) != NULL)
                {
                    if (CurArea.msgtype == FIDO)
                    {
                        hlink->replies[0] = UidToMsgn(m->msgnum);
                    }
                    MsgWriteHeader(hlink, WR_HEADER);
                    dispose(hlink);
                }
            }
        }
        break;

    case ABORT:
        ChoiceBox("", "Message was aborted.", "  Ok  ", NULL, NULL);
        scan_base = 1;
        break;
    }

    dispose(oldmsg);
    dispose(m);

    group_set_group(ogroup);

    if ((type & MT_ARC) || scan_base || CurArea.msgtype == SQUISH)
    {
        set_area(oarea);
        CurArea.current = link;
    }
    else
    {
        CurArea.current = link;
        CurArea.messages++;
        CurArea.last++;
    }
}

void change(void)
{
    int q = 0;
    unsigned long t = CurArea.current;
    int oarea = SW->grouparea;
    int ogroup = SW->group;

    if (CurArea.messages == 0 || !CurArea.status || !message)
        return;

    if (message->attrib.sent || message->scanned)
    {
        if (!sent_msg())
        {
            return;
        }
    }

    message->attrib.sent = 0;
    message->attrib.orphan = 0;
    message->attrib.local = 1;
    message->scanned = 0;
    do_lookup = FALSE;

    if (message->to.internet || message->to.bangpath)
    {
       char *oldptr = message->to.domain;
       message->to.domain = compose_internet_address(message->to.domain,
                              message->isto);
       release(oldptr);
       release(message->isto);
    }

    while (!q)
    {
        if (EditHeader(message) == Key_Esc)
        {
            if (confirm("Cancel?"))
            {
                if (t)
                {
                    CurArea.current = t;
                }
                return;
            }
        }
        else
        {
            q = 1;
        }
    }

    if (ST->editorName != NULL)
    {
        editRet = externalEditor(message);
    }
    else
    {
        editRet = editmsg(message, FALSE);
    }

    switch (editRet)
    {
    case SAVE:
        save(message);
        break;

    case ABORT:
        ChoiceBox("", "Message was aborted.", "  Ok  ", NULL, NULL);
        break;
    }

    group_set_group(ogroup);

    if (scan_base)  /* While changing, CC:s and XC:s could occur. */
    {
        set_area(oarea);
    }

    if (t)
    {
        CurArea.current = t;
    }
    message = KillMsg(message);
}

int ChangeAttrib(msg * m)
{
    WND *hWnd, *hCurr;
    int ch, done = 0;

    hCurr = WndTop();
    hWnd = WndPopUp(62, 13, SBDR | SHADOW, cm[IN_BTXT], cm[IN_NTXT]);

    /* put up some help for the befuddled user */

    WndTitle(" Message Attributes (Alt-Z: erase all)", cm[IN_NTXT]);

    WndWriteStr(2, 0, cm[IN_NTXT], "Private             <Alt-P>   Crash               <Alt-C>");
    WndWriteStr(2, 1, cm[IN_NTXT], "File Attach         <Alt-A>   Kill/Sent           <Alt-K>");
    WndWriteStr(2, 2, cm[IN_NTXT], "Hold                <Alt-H>   Direct              <Alt-D>");
    WndWriteStr(2, 3, cm[IN_NTXT], "Sent                <Alt-S>   Received            <Alt-R>");
    WndWriteStr(2, 4, cm[IN_NTXT], "Orphan              <Alt-O>   File Request        <Alt-F>");
    WndWriteStr(2, 5, cm[IN_NTXT], "Return Rcpt         <Alt-E>   Return Rcpt Request <Alt-Q>");
    WndWriteStr(2, 6, cm[IN_NTXT], "Audit Request       <Alt-I>   File Update Request <Alt-U>");
    WndWriteStr(2, 7, cm[IN_NTXT], "Local               <Alt-L>   Intransit (Forward) <Alt-T>");
    WndWriteStr(2, 8, cm[IN_NTXT], "Kill File when Sent <Alt-X>   Truncate File w. S. <Alt-Y>");
    WndWriteStr(2, 9, cm[IN_NTXT], "Archive when Sent   <Alt-J>   Immediate Delivery  <Alt-V>");
    WndWriteStr(2,10, cm[IN_NTXT], "Lock                <Alt-W>   Confirm Rcpt Req    <Alt-M>");
    WndWriteStr(2,11, cm[IN_NTXT], "Route via Zone Gate <Alt-G>   Route via Hub       <Alt-B>");
/*  WndWriteStr(2,12, cm[IN_NTXT], "           Zap (erase) all attributes  <Alt-Z>           "); */

    WndCurr(hCurr);  /* make parent window active so we can write to it */

    ShowAttrib(m);

    while (!done)
    {
        ch = GetKey();
        switch (ch)
        {
        case Key_Up:
        case Key_Dwn:
        case Key_Ent:
        case Key_Esc:
            done = 1;           /* we return these to the caller */
            break;

        case Key_A_A:
            m->attrib.attach ^= 1;
            break;

        case Key_A_P:
            m->attrib.priv ^= 1;
            break;

        case Key_A_C:
            m->attrib.crash ^= 1;
            break;

        case Key_A_U:
            m->attrib.ureq ^= 1;
            break;

        case Key_A_F:
            m->attrib.freq ^= 1;
            break;

        case Key_A_K:
            m->attrib.killsent ^= 1;
            break;

        case Key_A_H:
            m->attrib.hold ^= 1;
            break;

        case Key_A_D:
            m->attrib.direct ^= 1;
            break;

        case Key_A_Q:
            m->attrib.rreq ^= 1;
            break;

        case Key_A_E:
            m->attrib.rcpt ^= 1;
            break;

        case Key_A_R:
            m->attrib.rcvd ^= 1;
            break;

        case Key_A_S:
            m->attrib.sent ^= 1;
            break;

        case Key_A_O:
            m->attrib.orphan ^= 1;
            break;

        case Key_A_I:
            m->attrib.areq ^= 1;
            break;

        case Key_A_L:
            m->attrib.local ^= 1;
            break;

        case Key_A_T:
            m->attrib.forward ^= 1;
            break;

        case Key_A_X:
            m->attrib.kfs ^= 1;
            break;

        case Key_A_Y:
            m->attrib.tfs ^= 1;
            break;

        case Key_A_J:
            m->attrib.as ^= 1;
            break;

        case Key_A_V:
            m->attrib.immediate ^= 1;
            break;

        case Key_A_W:
            m->attrib.lock ^= 1;
            break;

        case Key_A_M:
            m->attrib.cfm ^= 1;
            break;

        case Key_A_G:
            m->attrib.zon ^= 1;
            break;

        case Key_A_B:
            m->attrib.hub ^= 1;
            break;


        case Key_A_Z:
            m->attrib.attach = 0;
            m->attrib.priv = 0;
            m->attrib.crash = 0;
            m->attrib.ureq = 0;
            m->attrib.freq = 0;
            m->attrib.killsent = 0;
            m->attrib.hold = 0;
            m->attrib.direct = 0;
            m->attrib.rreq = 0;
            m->attrib.rcpt = 0;
            m->attrib.rcvd = 0;
            m->attrib.sent = 0;
            m->attrib.orphan = 0;
            m->attrib.areq = 0;
            m->attrib.local = 0;
            m->attrib.forward = 0;
            m->attrib.kfs = 0;
            m->attrib.as = 0;
            m->attrib.immediate = 0;
            m->attrib.tfs = 0;
            m->attrib.lock = 0;
            m->attrib.cfm = 0;
            m->attrib.zon = 0;
            m->attrib.hub = 0;
            m->scanned = 0;
            break;

        default:
            switch ((toupper(ch & 0xff)))
            {
            case 'A':
                m->attrib.attach ^= 1;
                break;

            case 'P':
                m->attrib.priv ^= 1;
                break;

            case 'C':
                m->attrib.crash ^= 1;
                break;

            case 'U':
                m->attrib.ureq ^= 1;
                break;

            case 'F':
                m->attrib.freq ^= 1;
                break;

            case 'K':
                m->attrib.killsent ^= 1;
                break;

            case 'H':
                m->attrib.hold ^= 1;
                break;

            case 'D':
                m->attrib.direct ^= 1;
                break;

            case 'Q':
                m->attrib.rreq ^= 1;
                break;

            case 'E':
                m->attrib.rcpt ^= 1;
                break;

            case 'R':
                m->attrib.rcvd ^= 1;
                break;

            case 'S':
                m->attrib.sent ^= 1;
                break;

            case 'O':
                m->attrib.orphan ^= 1;
                break;

            case 'I':
                m->attrib.areq ^= 1;
                break;

            case 'L':
                m->attrib.local ^= 1;
                break;

            case 'T':
                m->attrib.forward ^= 1;
                break;

            case 'X':
                m->attrib.kfs ^= 1;
                break;

            case 'Y':
                m->attrib.tfs ^= 1;
                break;

            case 'J':
                m->attrib.as ^= 1;
                break;

            case 'V':
                m->attrib.immediate ^= 1;
                break;

            case 'W':
                m->attrib.lock ^= 1;
                break;

            case 'M':
                m->attrib.cfm ^= 1;
                break;

            case 'G':
                m->attrib.zon ^= 1;
                break;

            case 'B':
                m->attrib.hub ^= 1;
                break;

            case 'Z':
                m->attrib.attach = 0;
                m->attrib.priv = 0;
                m->attrib.crash = 0;
                m->attrib.ureq = 0;
                m->attrib.freq = 0;
                m->attrib.killsent = 0;
                m->attrib.hold = 0;
                m->attrib.direct = 0;
                m->attrib.rreq = 0;
                m->attrib.rcpt = 0;
                m->attrib.rcvd = 0;
                m->attrib.sent = 0;
                m->attrib.orphan = 0;
                m->attrib.areq = 0;
                m->attrib.local = 0;
                m->attrib.forward = 0;
                m->attrib.kfs = 0;
                m->attrib.as = 0;
                m->attrib.immediate = 0;
                m->attrib.tfs = 0;
                m->attrib.lock = 0;
                m->attrib.cfm = 0;
                m->attrib.zon = 0;
                m->attrib.hub = 0;
                m->scanned = 0;
                break;

            default:
                break;
            }
        }
        ShowAttrib(m);
    }

    WndCurr(hWnd);
    WndClose(hWnd);
    WndCurr(hCurr);
    return ch;  /* pass the exit key back to caller */
}

/*
 *  Looks up the alias list and returns the new name, and changes the
 *  passed address to the alias addres, if the passed name had a
 *  corresponding alias.
 */

static char *alias_lookup(ADDRESS * addr, char *isto)
{
    int l;
    char *name;

    /* search for a matching alias */

    for (l = 0; l < SW->otheraliases; l++)
    {
        if (!stricmp(aliaslist[l].alias, isto))
        {
            break;
        }
    }

    if (l >= SW->otheraliases)
    {
        return NULL;
    }
    else
    {
        name = xstrdup(aliaslist[l].name);
        addr->domain = NULL;
        *addr = aliaslist[l].addr;
        if (aliaslist[l].addr.domain != NULL)
        {
            addr->domain = xstrdup(aliaslist[l].addr.domain);
        }

/*      if (aliaslist[l].addr.internet == 1)
        {
            *addr = uucp_gate;
            addr->domain = NULL;
        }
        else
        {
            *addr = aliaslist[l].addr;
            if (aliaslist[l].addr.domain)
            {
                addr->domain = xstrdup(aliaslist[l].addr.domain);
            }
            else
            {
                addr->domain = NULL;
            }
        } */
    }
    return name;
}

/*
 *  Looks up the alias list to see if the passed alias has a subject.
 */

char *subj_lookup(char *isto)
{
    int l;

    for (l = 0; l < SW->otheraliases; l++)
    {
        if (!stricmp(aliaslist[l].alias, isto))
        {
            break;
        }
    }

    if (l >= SW->otheraliases || aliaslist[l].subj == NULL)
    {
        return NULL;
    }

    return xstrdup(aliaslist[l].subj);
}

/*
 *  Looks up name and addresses using alias, nodelist and fido userlist
 *  methods.  Returns a pointer to allocated memory containing the name
 *  to be used, and writes to the passed address variable.
 *
 *  Modifications by: Roland Gautschi (V7 lookup code - added flexibility).
 */

static char *addr_lookup(char *name, ADDRESS * tmp)
{
    char *nname;
    char xname[73];
    char namefound[73];
    char userlistname[73];
    int ret;

    tmp->fidonet = 1;
    tmp->internet = 0;
    tmp->bangpath = 0;

    if ((nname = alias_lookup(tmp, name)) == NULL)
    {
        nname = xstrdup(name);  /* couldn't find an alias, so.. */
        strcpy(xname, name);    /* make a copy, just in case */

        if (do_lookup)
        {
            /*
             *  Userlists are checked first, because they may have overides
             *  wanted by the user.
             */

            if (ST->fidolist != NULL)
            {
                strcpy (userlistname, name);
                *tmp = lookup(userlistname, ST->fidolist);
                if ((tmp->notfound) && (ST->userlist != NULL))
                {
                    *tmp = lookup(userlistname, ST->userlist);
                }
                if (!tmp->notfound)
                {
                   release(nname);
                   nname = xstrdup(userlistname);
                }
            }
            else
            {
                tmp->notfound = 1;
            }

            if (tmp->notfound == 1 && ST->nodepath != NULL)
            {
                /* Check to see if an address has been entered. */

                if (m_isdigit(xname[0]) || xname[0] == ':' ||
                  xname[0] == '/' || xname[0] == '.')
                {
                    *tmp = parsenode(xname);
                    if (tmp->notfound == 0)
                    {
                        if (v7lookupnode(tmp, xname) == NULL)
                        {
                            tmp->notfound = 1;
                        }
                        else
                        {
                            xfree(nname);
                            nname = xstrdup(xname);
                        }
                    }
                }

                /*
                 *  If the node still hasn't been found, then lookup
                 *  normally (using the entered name).
                 */

                if (tmp->notfound == 1)
                {
                    *tmp = v7lookup(xname);

                    /* If we found something, copy it. */

                    if (tmp->notfound == 0)
                    {
                        if (strcmp(nname, xname) != 0)
                        {
                            sprintf(namefound, "%s at %s found, change?",
                              xname, show_address(tmp));
                            ret = ChoiceBox(" Change Name ", namefound,
                              "Yes", "No", NULL);
                            if (ret == ID_ONE)
                            {
                                xfree(nname);
                                nname = xstrdup(xname);
                            }
                        }
                    }
                }
            }
        }
    }

    return nname;
}

static void GetAddress(ADDRESS * addr, char *from, char *subj)
{
    char *name, *str;

    if (do_lookup && (!CurArea.netmail))
    {
        do_lookup = FALSE;
    }

    name = addr_lookup(from, addr);

    /* Check for an Alias subject. */

    if ((str = subj_lookup(from)) != NULL)
    {
        strcpy(subj, str);
        release(str);
    }

    /* Check the name to see if it's a usenet message. */

    str = strchr(name, '@');
    if (str != NULL && (CurArea.netmail && CurArea.uucp))
    {
        addr->fidonet = 0;
        addr->notfound = 0;
        if (str == name)
        {
            addr->bangpath = 1;
            str++;
        }
        else
        {
            addr->internet = 1;
            str = name;
        }
        release(addr->domain);
        addr->domain = xstrdup(str);
        strcpy(from, str);
    }
    else if (str!=NULL && (CurArea.news || CurArea.uucp))
    {
        parse_internet_address(name, NULL, &str);
        strcpy(from, str);
    }
    else
    {
        strcpy(from, name);
    }
    release(name);
}

static int ChangeName(ADDRESS * addr, char *from, char *subj, int y)
{
    EVT e;
    char tmp[73], tmp2[73];
    static int disp;
    int ch = 0, pos, done = 0;

    strcpy(tmp2, subj);
    tmp[0] = '\0';
    if (addr->internet && addr->domain != NULL)
    {
        strncpy(tmp, addr->domain, sizeof tmp - 1);
    }
    else
    {
        if (addr->bangpath && addr->domain != NULL)
        {
            strncat(strcpy(tmp, "@"), addr->domain, sizeof tmp - 1);
        }
        else
        {
            if (from != NULL)
            {
                strncpy(tmp, from, sizeof tmp - 1);
            }
        }
    }

    pos = strlen(tmp);
    disp = 1;

    while (!done)
    {
        if (addr->bangpath || addr->internet || CurArea.news || CurArea.uucp)
        {
            ch = WndGetLine(8, y, maxx - 28, tmp, cm[CM_ETXT], &pos, disp, 0, disp, &e);
        }
        else
        {
            ch = WndGetLine(8, y, 36, tmp, cm[CM_ETXT], &pos, disp, 0, disp, &e);
        }

        switch (e.msgtype)
        {
        case WND_WM_CHAR:
            switch (ch)
            {
            case Key_Esc:
                 done = 1;
                 break;

            case Key_Up:
            case Key_Dwn:
            case Key_Rgt:
            case Key_Lft:
            case Key_Ent:
                if (addr->fidonet)
                {
                    if (strlen(tmp) > 36)
                    {
                        tmp[35] = '\0';
                    }
                }
                GetAddress(addr, tmp, tmp2);
                strcpy(from, tmp);
                if (strcmp(subj, tmp2))
                {
                    strcpy(subj, tmp2);
                    ShowSubject(subj);
                }
                done = 1;
                break;

            default:
                break;
            }
            break;

        default:
            break;
        }
        disp = 0;
    }

    return ch;
}

int ChangeAddress(ADDRESS * addr, int y, int nm_len)
{
    EVT e;
    char tmp[41];
    int ch, pos, done = 0, disp = 1;

    strncpy(tmp, show_address(addr), 40);
    release(addr->domain);

    pos = strlen(tmp);

    while (!done)
    {
        ch = WndGetLine(8 + nm_len + 2, y, 50 - nm_len, tmp, cm[CM_ETXT], &pos, disp, 0, disp, &e);

        switch (e.msgtype)
        {
        case WND_WM_CHAR:
            switch (ch)
            {
            case Key_Esc:
                done = 1;
                break;

            case Key_Up:
            case Key_Dwn:
            case Key_Rgt:
            case Key_Lft:
            case Key_Ent:
                *addr = parsenode(tmp);
                done = 1;
                break;

            default:
                break;
            }
            break;

        default:
            break;
        }
        disp = 0;
    }

    return ch;
}

int ChangeSubject(char *subj)
{
    EVT e;
    struct _dta fileinfo;
    char tmp[73];
    int ch, pos, ch2, done = 0, disp = 1;

    strncpy(tmp, subj, sizeof tmp - 1);

    pos = strlen(tmp);

    while (!done)
    {
        ch = WndGetLine(8, 3, 72, tmp, cm[CM_ETXT], &pos, disp, 0, disp, &e);

        switch (e.msgtype)
        {
        case WND_WM_CHAR:
            switch (ch)
            {
            case Key_Esc:
                done = 1;
                break;

            case Key_Up:
            case Key_Dwn:
            case Key_Rgt:
            case Key_Lft:
            case Key_Ent:
                strcpy(subj, tmp);
                if (strlen(subj) > 3 && *(subj + 1) == ':' &&
                  (*(subj + 2) == '\\' || *(subj + 2) == '/'))
                {
                    /*
                     *  If the file doesn't exist, then ask if user wants
                     *  to continue...
                     */

                    if (dir_findfirst(subj, DIR_NORMAL, &fileinfo) != 0)
                    {
                        ch2 = ChoiceBox("", "WARNING: File doesn't exist! Attach anyway?",
                          "Yes", "No", NULL);

                        if (ch2 == ID_ONE)
                        {
                            EdMsg->attrib.attach = 1;
                        }
                        else
                        {
                            /* continue editing */
                            continue;
                        }
                    }
                    else
                    {
                        EdMsg->attrib.attach = 1;
                    }
                }
                done = 1;
                break;

            default:
                break;
            }
            break;

        default:
            break;
        }
        disp = 0;
    }
    ShowSubject(subj);
    return ch;
}

static char *getstring(char *buf)
{
    if (strlen(buf) > 0)
    {
        return xstrdup(buf);
    }
    else
    {
        return NULL;
    }
}

char *construct_uucpname(const char *cpdomain)
{
  if (strcmp(ST->uucpgate, "*") != 0)
  {
     return xstrdup(ST->uucpgate);
  }
  else
  {
     char *cpname;

     parse_internet_address(cpdomain, NULL, &cpname);
     return cpname;
  }
}

#define FD_FROM   0
#define FD_FADD   1
#define FD_TO     2
#define FD_TADD   3
#define FD_SUBJ   4
#define FD_ATTR   5

int EditHeader(msg * m)
{
    char tmp[80], tmp2[80];
    int field = 2;
    int ch = 0, done = 0;

    /*
     *  This is naughty, but it allows the functions to access the
     *  current message in those special cases.
     */

    EdMsg = m;

    ShowMsgHeader(m);

    while (!done)
    {
        strcpy(tmp, "");
        strcpy(tmp2, "");

        switch (field)
        {
        case FD_FROM:
            ShowNameAddress(m->isfrom, &m->from, 1, 0, 1);
            if (m->isfrom)
            {
                strcpy(tmp, m->isfrom);
                release(m->isfrom);
            }
            if (m->subj)
            {
                strcpy(tmp2, m->subj);
                release(m->subj);
            }
            ch = ChangeName(&m->from, tmp, tmp2, 1);
            m->isfrom = getstring(tmp);
            m->subj = getstring(tmp2);

            if (m->from.internet || m->from.bangpath)
            {
                char *oldname=m->isfrom;

                m->isfrom = construct_uucpname(m->isfrom);
                release (oldname);

            }
            ShowNameAddress(m->isfrom, &m->from, 1, 0, 1);
            break;

        case FD_FADD:
            if (CurArea.netmail && m->from.fidonet)
            {
                ch = ChangeAddress(&m->from, 1,
                                   (m->isfrom) ? strlen(m->isfrom) : 0);
                m->from.dontmatch = 1; /* no more aka matching here, please */
            }
            ShowNameAddress(m->isfrom, &m->from, 1, 0, 0);
            break;

        case FD_TO:
            if (CurArea.news)
            {
                release(m->isto);
                m->isto = xstrdup("All");
                m->to = uucp_gate;
                ch = Key_Dwn;
            }
            else
            {
                ShowNameAddress(m->isto, &m->to, 2, 0, 1);
                if (m->isto)
                {
                    strcpy(tmp, m->isto);
                    release(m->isto);
                }
                if (m->subj)
                {
                    strcpy(tmp2, m->subj);
                    release(m->subj);
                }
                ch = ChangeName(&m->to, tmp, tmp2, 2);
                m->isto = getstring(tmp);
                m->subj = getstring(tmp2);
            }

            /* generate the UUCP user name, if e-mail address was entered */
            if (m->to.internet || m->to.bangpath)
            {
                char *oldname = m->isto;

                m->isto = construct_uucpname(m->isto);
                release(oldname);
            }

            ShowNameAddress(m->isto, &m->to, 2, 0, 1);
            break;

        case FD_TADD:
            if (CurArea.netmail && m->to.fidonet)
            {
                ch = ChangeAddress(&m->to, 2, (m->isto) ? strlen(m->isto) : 0);
            }

            /* AKA matching, only for netmails */
            if (CurArea.netmail)
            {
                if (akamatch(&(m->from), &(m->to)))
                {
                    /* we found a better address, redisplay it */
                    ShowNameAddress(m->isfrom, &m->from, 1, 0, 0);
                }
            }
            break;

        case FD_SUBJ:
            if (m->subj)
            {
                strcpy(tmp, m->subj);
                release(m->subj);
            }
            ch = ChangeSubject(tmp);
            m->subj = getstring(tmp);
            break;

        case FD_ATTR:
            ch = ChangeAttrib(m);
            break;

        default:
            break;
        }

        if (ch == Key_Esc)
        {
            done = 1;
        }
        else if (ch == Key_Up)
        {
            field--;

            if (field < 0)
            {
                field = 5;
            }

            /* Handle the cases where going up stuffs the address showing. */

            switch (field)
            {
            case FD_FADD:
                ShowNameAddress(m->isto, &m->to, 2, 0, 0);
                break;

            case FD_ATTR:
                ShowNameAddress(m->isfrom, &m->from, 1, 0, 0);
                break;

            default:
                break;
            }
        }
        else if (ch == Key_Dwn || ch == Key_Ent)
        {
            if (field == 5 && ch == Key_Ent)
            {
                break;
            }

            field++;

            if (field > 5)
            {
                field = 0;
            }

            continue;
        }
    }

    ShowMsgHeader(m);
    cursor(0);

    return 0;
}

/*
 *  Clears all the attributes of a message, leaving Local set to TRUE.
 *  Not static; is called from maintmsg.c module.
 */

void clear_attributes(struct _attributes *h)
{
    memset(h, '\0', sizeof *h);
    h->crash = CurArea.crash;
    h->priv = CurArea.priv;
    h->killsent = CurArea.killsent;
    h->hold = CurArea.hold;
    h->direct = CurArea.direct;
    h->local = 1;
}

/* Contains the name and address of the recipient of the message */

typedef struct
{
    char *name;
    ADDRESS addr;
}
NA;

static void CreateNormalCC(msg * m, NA * names[])
{
    LINE *current;
    char dfn[256];

    int i = 0;

    m->text = lineins(m->text);
    m->text->text = xstrdup("\n");
    m->text = lineins(m->text);

    sprintf(dfn, "* Original to:   %s\n", names[i]->name);

    i++;

    m->text->text = xstrdup(dfn);

    strcpy(dfn, "* Carbon Copies: ");

    current = m->text;
    current = lineins(current->next);
    current->text = xstrdup("\n");

    while (names[i] != NULL)
    {
        if (strlen(names[i]->name) + strlen(dfn) >= SW->rm)
        {
            strcat(dfn, "\n");
            release(current->text);

            current->text = xstrdup(dfn);
            current = lineins(current->next);
            current->text = xstrdup("\n");

            strcpy(dfn, "                 ");
        }
        strcat(dfn, names[i]->name);

        i++;

        if (names[i] != NULL)
        {
            strcat(dfn, ", ");
        }
    }

    if (*(current->text) == '\n')
    {
        release(current->text);
        current->text = xstrdup(dfn);
    }
}

static void CreateVerboseCC(msg * m, NA * names[])
{
    int i = 0;
    char dfn[256];

    m->text = lineins(m->text);
    m->text->text = xstrdup("\n");

    while (names[i] != NULL)
    {
        if (strchr(names[i]->name, '@') == NULL)
        {
            sprintf(dfn, "     %s at %s\n", names[i]->name, show_address(&names[i]->addr));
        }
        else
        {
            sprintf(dfn, "     %s\n", names[i]->name);
        }

        m->text = lineins(m->text);
        m->text->text = xstrdup(dfn);

        i++;
    }

    m->text = lineins(m->text);
    m->text->text = xstrdup("* Carbon Copies to:\n");
}

/*
 *  Gets the CC:s from a response file.
 */

static void GetFileCCs(char *file, NA * names[], int *idx)
{
    FILE *fp;
    char buf[256];
    char *s, *c, *t;
    int i;

    i = *idx;

    fp = fopen(file, "r");
    if (fp == NULL)
    {
        return;
    }

    while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
    {
        s = buf;
        while (*s && m_isspace(*s))
        {
            s++;
        }

        if (!*s || *s == ';')
        {
            continue;
        }

        t = s + strlen(s) - 1;
        while (t > s && m_isspace(*t))
        {
            t--;
        }

        if (t == s)
        {
            continue;
        }
        else
        {
            if (*(t + 1))
            {
                t++;
            }
            *t = '\0';
        }

        names[i] = xcalloc(1, sizeof(NA));

        if ((c = strchr(s, '@')) != NULL)
        {
            /* Then we have an internet/bangpath. */

            names[i]->addr.fidonet = 0;
            if (c == s)
            {
                names[i]->addr.bangpath = 1;
                c++;
            }
            else
            {
                names[i]->addr.internet = 1;
                c = s;
            }
            names[i]->name = xstrdup(c);
            names[i]->addr.domain = xstrdup(c);
        }
        else
        {
            c = strchr(s, '!');

            if (c)
            {
                *c = '\0';
                names[i]->addr = parsenode(++c);
                names[i]->name = xstrdup(s);
            }
            else
            {
                names[i]->name = addr_lookup(s, &names[i]->addr);

                /* UUCP address from alias? */

                if ((c = strchr(names[i]->name, '@')) != NULL)
                {
                    names[i]->addr.fidonet = 0;
                    if (c == names[i]->name)
                    {
                        names[i]->addr.bangpath = 1;
                        c++;
                    }
                    else
                    {
                        names[i]->addr.internet = 1;
                        c = names[i]->name;
                    }
                    names[i]->addr.domain = xstrdup(c);
                }
            }
        }

        if (names[i]->addr.notfound)
        {
            names[i]->addr = CurArea.addr;
            if (CurArea.addr.domain)
            {
                names[i]->addr.domain = xstrdup(CurArea.addr.domain);
            }
        }
        i++;
    }
    names[i] = NULL;
    *idx = i;
    fclose(fp);
}

/*
 *  Saves a message if the first line does not contain "CC:" or "XC:",
 *  else it continues and either makes a carbon copy (or copies) or
 *  crosspost(s) of the message.
 *
 *  The idea here is: First get the addresses from the CC: list, if there
 *  is one (if not, check for XC: and leave).  If so, we then make up the
 *  text for the message (in a loop), then we go and save the messages using
 *  the names and addresses from the CC: list.
 */

void save(msg * m)
{
    LINE *current;
    NA *names[128];
    int num = 1, i;
    unsigned long k;
    int verbose_cc = 0;
    int blind_cc = 0;
    char *s, *t, *sp;

    current = m->text;
    do_lookup = TRUE;

/*    if (m->new) */
    {
        if (!strncmpi(current->text, "xc:", 3) && !CurArea.netmail)
        {
            if (m == message)
            {
                message = NULL;
                scan_base = 1;
            }
            crosspost(m);
            return;
        }
    }
    current = m->text;
    s = current->text;

    if (!strncmpi(current->text, "bc:", 3))
    {
        blind_cc = 1;
    }
    else if (!strncmpi(current->text, "hc:", 3))
    {
        blind_cc = 1;
    }
    else if (!strncmpi(current->text, "vc:", 3))
    {
        verbose_cc = 1;
    }
    else if (strncmpi(current->text, "cc:", 3))
    {
        writemsg(m);
        scan_base = TRUE;
        return;
    }
    release(m->reply);

    memset(names, 0, sizeof names);

    names[0] = xmalloc(sizeof(NA));

    names[0]->addr = m->to;
    if (m->to.internet || m->to.bangpath)
    {
        names[0]->name = compose_internet_address(m->to.domain, m->isto);
        names[0]->addr = m->to;
        names[0]->addr.domain = xstrdup(names[0]->name);
    }
    else
    {
        names[0]->name = xstrdup(m->isto);
        if (m->to.domain)
        {
            names[0]->addr.domain = xstrdup(m->to.domain);
        }
    }

    m->attrib.sent = 1;
    if (SW->savecc && SW->rawcc)
    {
        writemsg(m);
        m->attrib.killsent = 1;
    }
    m->attrib.sent = 0;


    /* Get names and addresses from CC: list. */

    while (current && *s && !strncmpi(s + 1, "c:", 2))
    {
        s += 3;
        while (*s && m_isspace(*s))
        {
            s++;
        }

        if (*s == '~')
        {
            sp = strchr(s, '\n');
            if (sp != NULL)
            {
                *sp = '\0';
            }
            GetFileCCs(s + 1, names, &num);
        }
        else
        {
            while (*s != '\0' && s != NULL)
            {
                while (*s && m_isspace(*s))
                {
                    s++;
                }
                if (*s == '\0')
                {
                    break;
                }

                /* get next name on line */

                t = strchr(s, ',');

                if (t)
                {
                    *t = '\0';
                }
                else
                {
                    t = strchr(s, '\n');
                    if (t)
                    {
                        *t = '\0';
                    }
                    else
                    {
                        t = s + strlen(s) - 1;
                    }
                }

                names[num] = xcalloc(1, sizeof(NA));

                /* UUCP address? */

                if ((sp = strchr(s, '@')) != NULL)
                {
                    names[num]->addr.fidonet = 0;
                    if (sp == s)
                    {
                        names[num]->addr.bangpath = 1;
                    }
                    else
                    {
                        names[num]->addr.internet = 1;
                    }
                    names[num]->name = xstrdup(s);
                    names[num]->addr.domain = xstrdup(s);
                }
                else
                {
                    /* look for address */

                    sp = strchr(s, '!');

                    if (sp)
                    {
                        *sp = '\0';
                    }

                    if (sp)
                    {
                        names[num]->addr = parsenode(++sp);
                        names[num]->name = xstrdup(s);
                    }
                    else
                    {
                        names[num]->name = addr_lookup(s, &names[num]->addr);

                        /* UUCP address from alias? */

                        sp = strchr(names[num]->name, '@');
                        if (sp != NULL)
                        {
                            names[num]->addr.fidonet = 0;
                            if (sp == names[num]->name)
                            {
                                names[num]->addr.bangpath = 1;
                                sp++;
                            }
                            else
                            {
                                names[num]->addr.internet = 1;
                                sp = names[num]->name;
                            }
                            names[num]->addr.domain = xstrdup(sp);
                        }
                    }
                }
                s = ++t;
                num++;
            }
        }
        if (current->next)
        {
            current = current->next;
        }
        else
        {
            i = 0;
            while (names[i] != NULL)
            {
                if (names[i]->name)
                {
                    release(names[i]->name);
                }
                if (names[i]->addr.domain)
                {
                    release(names[i]->addr.domain);
                }
                release(names[i]);
            }
            return;
        }

        s = current->text;
        m->text = current;
        release(current->prev->text);
        release(current->prev);
        current->prev = NULL;
    }

    names[num] = NULL;

    if (blind_cc == 0)
    {
        if (verbose_cc)
        {
            CreateVerboseCC(m, names);
        }
        else
        {
            CreateNormalCC(m, names);
        }
    }

    i = 0;

    release(m->msgid);
    k = MsgnToUid(CurArea.messages) + 1;
    m->new = 1;

    while (names[i] != NULL)
    {
        release(m->isto);
        release(m->to.domain);

        m->to = names[i]->addr;

        if (names[i]->addr.domain)
        {
            m->to.domain = xstrdup(names[i]->addr.domain);
        }

        if (names[i]->addr.internet || names[i]->addr.bangpath)
        {
            m->isto = construct_uucpname(m->to.domain);
        }
        else
        {
            m->isto = xstrdup(names[i]->name);
        }


        /* for *.msg bases - get the real msg num */

        m->msgnum = k++;
        writemsg(m);

        if (SW->savecc && !SW->rawcc)
        {
            m->attrib.killsent = 1;
        }

        release(names[i]->name);
        release(names[i]->addr.domain);
        i++;
    }

    scan_base = TRUE;
}

static void crosspost(msg * m)
{
    LINE *current;
    char *s, *t;
    char dfn[128];
    char *arean[128];
    unsigned int num = 0, i, k;

    current = m->text;
    s = current->text;

    arean[num] = xstrdup(CurArea.tag);
    num++;

    while (current && *s && !strncmpi(s, "xc:", 3))
    {
        s += 3;

        while (*s && m_isspace(*s))
        {
            s++;
        }

        while (*s != '\0' && s != NULL)
        {
            t = strchr(s, ',');
            if (t)
            {
                *t = '\0';
            }
            else
            {
                t = strchr(s, '\n');
                if (t)
                {
                    *t = '\0';
                }
                else
                {
                    t = s + strlen(s) - 1;
                }
            }
            while (*s && m_isspace(*s))
            {
                s++;
            }
            arean[num] = xstrdup(strupr(s));

            if (arean[num++] == NULL)
            {
                break;
            }

            s = ++t;
        }
        if (current->next)
        {
            current = current->next;
        }
        else
        {
            i = 0;
            while (arean[i] != NULL)
            {
                release(arean[i++]);
            }
            return;
        }
        s = current->text;
        m->text = current;

        if (current->prev->text)
        {
            release(current->prev->text);
        }

        release(current->prev);
        current->prev = NULL;
    }

    arean[num] = NULL;

    if (strncmpi(s, "cc:", 3) != 0)
    {
        m->text = lineins(m->text);
        m->text->text = xstrdup("\n");
        current = m->text;
        i = 0;
        strcpy(dfn, " * Crossposted in area ");

        while (arean[i] != NULL)
        {
            if (strlen(arean[i]) + 3 + strlen(dfn) >= SW->rm)
            {
                strcat(dfn, "\n");
                release(current->text);
                current->text = xstrdup(dfn);
                current = lineins(current->next);
                current->text = xstrdup("\n");
                strcpy(dfn, "                       ");
            }
            strcat(dfn, arean[i]);
            i++;
            if (arean[i] != NULL)
            {
                strcat(dfn, ", ");
            }
        }

        if (*(current->text) == '\n')
        {
            release(current->text);
            strcat(dfn,"\n");
            current->text = xstrdup(dfn);
        }
    }

    i = 0;
    while (arean[i] != NULL)
    {
        for (k = 0; k < SW->areas; k++)
        {
            if (arealist[k].tag && !stricmp(arean[i], arealist[k].tag))
            {
                break;
            }
        }
        if (k == SW->areas || !arealist[k].echomail)
        {
            release(arean[i]);
            i++;
            continue;
        }
        set_nongrouped_area(k);
        release(m->from.domain);
        m->from = CurArea.addr;

        if (CurArea.addr.domain)
        {
            m->from.domain = xstrdup(CurArea.addr.domain);
        }

        if (CurArea.status)
        {
            if (i > 0)
            {
                m->new = 1;
            }

            if (m->new)
            {
                m->msgnum = MsgnToUid(CurArea.messages) + 1;
            }

            if (strncmpi(s, "cc:", 3) == 0)
            {
                save(m);
            }
            else
            {
                writemsg(m);
            }
        }
        release(arean[i]);
        i++;
    }
    scan_base = TRUE;           /* reopen the base when we get back */
}

static void spawn_command(char *cmdline)
{
#if defined(MSDOS) && !defined(NODOSSWAP) && !defined(__FLAT__)
    int rc;
    char swapfn[PATHLEN];
    char **envp = environ, **env, *envbuf, *envptr, *ep;
    int swapping = USE_ALL | HIDE_FILE | DONT_SWAP_ENV;
    int envlen = 0;

    if (envp != NULL)
    {
        for (env = envp; *env != NULL; env++)
        {
            envlen += strlen(*env) + 1;
        }
    }

    if (envlen)
    {
        envlen = (envlen + 32) & 0xfff0;
        envbuf = xmalloc(envlen);
        envptr = envbuf;

        if (FP_OFF(envptr) & 0x0f)
        {
            envptr += 16 - (FP_OFF(envptr) & 0x0f);
        }

        ep = envptr;

        for (env = envp; *env != NULL; env++)
        {
            strcpy(ep, *env);
            ep = ep + strlen(*env) + 1;
        }
        *ep = 0;
    }
    if (ST->swap_path)
    {
        sprintf(swapfn, "%s\\%s", ST->swap_path, SWAP_FILENAME);
    }
    else
    {
        strcpy(swapfn, SWAP_FILENAME);
    }

    rc = prep_swap(swapping, swapfn);

    if (rc > -1)
    {
        rc = do_spawn(swapping, ST->comspec, cmdline, envlen, envptr);
    }
    else
    {
        fprintf(stderr, "\nError occured during do_spawn(); rc=%d. Press Enter to return...", rc);
        while (GetKey() != 13)
        {
        }
    }
#else
    system(cmdline);
#endif
}

static int externalEditor(msg * m)
{
    FILE *fq;
    LINE *current;
    static char cmd[200];
    WND *hWnd, *hCurr;
    static char linebuf[1000];
    size_t linelen;
    int hardquote, linenum;
    static char tmpfnm[] = "msged.tmp";
    int doformat, statready;
    struct stat ostat, nstat;

    doformat = SW->extformat;
    hardquote = 0;
    current = m->text;
    fq = fopen(tmpfnm, "w");
    if (fq != NULL)
    {
        while (current != NULL)
        {
            if (current->text != NULL)
            {
                fputs(current->text, fq);
            }
            current = current->next;
        }
        fclose(fq);
        statready = stat(tmpfnm, &ostat) == 0;

        maxx = term.NCol;
        maxy = term.NRow;

        hCurr = WndTop();
        hWnd = WndOpen(0, 0, term.NCol - 1, term.NRow - 1, NBDR, 0, cm[CM_NTXT]);
        WndCurr(hWnd);
        MouseOFF();
        cursor(1);

#if defined(MSDOS) && !defined(NODOSSWAP) && !defined(__FLAT__)
        sprintf(cmd, "/c %s %s", ST->editorName, tmpfnm);
#else
        sprintf(cmd, "%s %s", ST->editorName, tmpfnm);
#endif
        spawn_command(cmd);

        cursor(0);
        MouseON();
        WndClose(hWnd);
        WndCurr(hCurr);

        if (statready && stat(tmpfnm, &nstat) == 0 &&
          ostat.st_mtime == nstat.st_mtime && ostat.st_size == nstat.st_size)
        {
            /*
             *  Old and new timestamps and file sizes are identical, so no
             *  modifications were performed.
             */
            return ABORT;
        }

        m->text = clearbuffer(m->text);

        fq = fopen(tmpfnm, "r");
        if (fq != NULL)
        {
            linenum = 0;
            while (fgets(linebuf, sizeof linebuf, fq) != NULL)
            {
                linenum++;
                if (*(linebuf + strlen(linebuf) - 1) != '\n')
                {
                    strcat(linebuf, "\n");
                }
                if (current == NULL)
                {
                    m->text = xcalloc(1, sizeof(LINE));
                    current = m->text;
                }
                else
                {
                    current = InsertLine(current);
                }
                if (doformat)
                {
                    if (!hardquote)
                    {
                        linelen = strlen(linebuf);

                        if (strcmp(linebuf, "<<\n") == 0)
                        {
                            /* begin temporary hardquoting */
                            if (linenum == 1)
                            {
                                *linebuf = '\0';
                            }
                            else
                            {
                                strcpy(linebuf, "\n");
                            }
                            hardquote = 1;
                        }
                        else if (linebuf[linelen - 2] == '<' && linebuf[linelen - 3] == '<')
                        {
                            /*
                             *  The following checks for << at end of line
                             *  and replaces them with CR and \0.
                             */
                            linebuf[linelen - 3] = '\r';
                            linebuf[linelen - 2] = '\0';
                        }
                        else
                        {
                            /*
                             *  If not a quote and not prefixed with a
                             *  'special' character and greater than
                             *  width - 30? then remove CR
                             */

                            if (linelen > term.NCol - 30 && !isquote(linebuf) &&
                              !strchr("\01 !@#$%^&*~-_+=:;,./", *linebuf))
                            {
                                linebuf[linelen - 1] = '\0';
                            }
                        }
                    }
                    else
                    {
                        if (strcmp(linebuf, "<<\n") == 0)
                        {
                            /* end temporary hardquoting */
                            strcpy(linebuf, "\n");
                            hardquote = 0;
                        }
                    }
                }
                current->text = xstrdup(linebuf);
            }
            fclose(fq);
        }
    }

    if (current == NULL)
    {
        return ABORT;
    }

    return SAVE;
}

