#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: reply.c,v 4.94 1994/10/06 22:13:29 mikes Exp $";
#endif
/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Builiding, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989-1994  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  
   Pine and Pico are trademarks of the University of Washington.
   No commercial use of these trademarks may be made without prior
   written permission of the University of Washington.

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  Revision: 2.13                             *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

/*======================================================================
    reply.c
   
   Code here for forward and reply to mail
   A few support routines as well

  This code will forward and reply to MIME messages. The Pine composer
at this time will only support non-text segments at the end of a
message so, things don't always come out as one would like. If you
always forward a message in MIME format, all will be correct.  Forwarding
of nested MULTIPART messages will work.  There's still a problem with
MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
the equivalent parts.  Ideally, we should probably such segments as a 
single attachment when forwarding/replying.  It would also be real nice to
flatten out the nesting in the composer so pieces inside can get snipped.

The evolution continues...

  =====*/


#include "headers.h"


#ifdef ANSI
char	*generate_in_reply_to(ENVELOPE *);
int	 fetch_contents(MAILSTREAM *, long, BODY *, BODY *);
char	*partno(BODY *, BODY *);
void	 format_text_header(ENVELOPE *, gf_io_t, char *);
int	 full_text_header(MAILSTREAM *, long, gf_io_t, char *);
void	 reply_delimiter(ENVELOPE *, gf_io_t);
ADDRESS	*reply_cpy_adr(struct pine *, ADDRESS *, ADDRESS *);
int	 reply_match_adrs(ADDRESS *, ADDRESS *);
char	*reply_subject(char *, char *);
void	 forward_delimiter(gf_io_t);
char	*forward_subject(struct pine *, long);

#else
char	*generate_in_reply_to();
int	 fetch_contents();
char	*partno();
void	 format_text_header();
int	 full_text_header();
ADDRESS	*reply_cpy_adr();
int	 reply_match_adrs();
char	*reply_subject();
void	 reply_delimiter();
void	 forward_delimiter();
char	*forward_subject();
#endif


/*
 * Little defs to keep the code a bit neater...
 */
#define	FRM_PMT	"Use \"Reply to:\" address instead of \"From:\" address"
#define	ALL_PMT		"Reply to all recipients"
#define	NEWS_PMT	"Post follow-up message to news group(s)"
#define	BUG_PMT		"Attach current message to report"

/*
 * standard type of storage object used for body parts...
 */
#ifdef	DOS
#define		  PART_SO_TYPE	TmpFileStar
#else
#define		  PART_SO_TYPE	CharStar
#endif



/*----------------------------------------------------------------------
        Fill in an outgoing message for reply and pass off to send

    Args: pine_state -- The usual pine structure

  Result: Reply is formatted and passed off to composer/mailer

Reply

   - put senders address in To field
   - search to and cc fields to see if we aren't the only recipients
   - if other than us, ask if we should reply to all.
   - if answer yes, fill out the To and Cc fields
   - fill in the fcc argument
   - fill in the subject field
   - fill out the body and the attachments
   - pass off to pine_send()
  ---*/
void
reply(pine_state)
     struct pine *pine_state;
{
    ADDRESS  **to_tail, **cc_tail, *atmp;
    ENVELOPE   *env, *outgoing;
    BODY       *body, *orig_body;
    PART       *part;
    void       *msgtext;
    char       *sig, *tp, *prefix, *refs = NULL, *fcc = NULL;
    long        msgno, totalm, *seq = NULL;
    int         i, include_text = 0, reply_ret = 0, cc_ret = 0,
		times = -1, ret, warned = 0;
    gf_io_t     pc;
#if	defined(DOS) && !defined(_WINDOWS)
    char *reserve;
#endif

    outgoing = mail_newenvelope();
    totalm   = mn_total_cur(pine_state->msgmap);
    sig      = get_signature();
    prefix   = "> ";
    seq	     = (long *)fs_get(((size_t)totalm + 1) * sizeof(long));

    dprint(4, (debugfile,"\n - reply (%s msgs) -\n", comatose(totalm)));

    /*
     * For consistency, the first question is always "include text?"
     */
    if(F_ON(F_AUTO_INCLUDE_IN_REPLY, pine_state)){
	include_text = 1;
    }
    else{
	sprintf(tmp_20k_buf, "Include %s%soriginal message%s in Reply",
		(totalm > 1L) ? comatose(totalm) : "",
		(totalm > 1L) ? " " : "",
		(totalm > 1L) ? "s" : "");

	if((ret = want_to(tmp_20k_buf, 'n', 'x', NO_HELP, 0, 0)) == 'x'){
	    q_status_message(1, 0, 3,"Reply cancelled");
	    goto done_early;
	}
	else
	  include_text = ret == 'y';
    }

    /*---- Set up the To:, Cc:, and Subject: fields ----*/
    to_tail		  = &outgoing->to;
    cc_tail		  = &outgoing->cc;
    outgoing->to	  = (ADDRESS *)NULL;
    outgoing->cc	  = (ADDRESS *)NULL;
    outgoing->subject	  = NULL;
    ret			  = 0;
    for(msgno = mn_first_cur(pine_state->msgmap);
	msgno > 0L;
	msgno = mn_next_cur(pine_state->msgmap)){

	/*--- Grab current envelope ---*/
	env = mail_fetchstructure(pine_state->mail_stream,
			    seq[++times] = mn_m2raw(pine_state->msgmap, msgno),
			    NULL);
	if(!env) {
	    q_status_message1(1,2,4,
			      "Error fetching message %s. Can't reply to it.",
			      long2string(msgno));
	    goto done_early;
	}

	/*
	 * If we haven't asked before, and we're replying to one
	 * messsage (an agg reply in a non-news folder may not have
	 * newsgroups in all the members or even different news groups)
	 * or an agg reply in a news folder, and the envelope has a
	 * newsgroup, ask if the user is posting a follow-up article.
	 */
	if(!ret && (totalm == 1L || IS_NEWS(pine_state->mail_stream))
	   && env->newsgroups && env->newsgroups[0]
	   && (ret = want_to(NEWS_PMT, 'n', 'x', NO_HELP, 0, 0)) == 'y'){
	    /*========= Follow up to a news group ============*/
	    outgoing->newsgroups = cpystr(env->newsgroups);

	    /* Seems unlikey anyone will want to both post
	       news and follow up to the author of the posting assuming
	       the author reads news.  Well maybe not so unlikely, but
	       potentially five questions before posting seems excessive:
	       1. include orig     2. Follow up      3. Also e-mail
	       4. use reply-to     5. all recipients
	       */

/*
 * BUG: we need to decide what to do if reply in mixed news/mail folder
 * and some message have newsgroups and some don't or the ones that do
 * don't all match.
 */

	    /*---- Set up the References: field ----*/
	    if(totalm == 1L && env->message_id && (i=strlen(env->message_id))){
		static char *fields[] = {"References", NULL};
		int   l;
		char *p, *q, *tmp;

		if((tmp = xmail_fetchheader_lines(pine_state->mail_stream, 
						  seq[times], fields))
		   && (p = strchr(tmp, ':'))){
		    for(++p; isspace(*p); p++) /* find start of id list */
		      ;

		    if((l = strlen(p)) > 2){    /* if anything left... */
			p[l-2] = '\0';		/* blat CRLF */

			while((l = strlen(p)) + i + 1 > 512
			      && (q = strstr(p, "> <")))
			  p = q + 2;

			refs = fs_get(i + l + 2);
			sprintf(refs, "%s %s", p, env->message_id);
		    }
		}
		else
		  refs = cpystr(env->message_id);

		if(tmp)
		  fs_give((void **)&tmp);
	    }
	}
	else if(ret == 'x') {
	    q_status_message(1, 0, 3,"Reply cancelled");
	    goto done_early;
	}

	if(!outgoing->newsgroups){
	    /*
	     * Always use the reply-to if we're replying to more than one 
	     * msg...
	     */
	    if(env->reply_to && !reply_match_adrs(env->reply_to, env->from)
	       && (totalm > 1L
		   || (reply_ret = want_to(FRM_PMT,'y','x',NO_HELP,0,0))=='y'))
	      *to_tail = reply_cpy_adr(NULL, outgoing->to, env->reply_to);
	    else
	      *to_tail = reply_cpy_adr(NULL, outgoing->to, env->from);

	    if(reply_ret == 'x') {
		q_status_message(1, 0, 3, "Reply cancelled");
		goto done_early;
	    }

	    while(*to_tail)			/* stay on last address */
	      to_tail = &(*to_tail)->next;

	    /*------ check to see if we aren't the only recipient -----------*/
	    for(atmp = env->to; atmp; atmp = atmp->next)
	      if(!address_is_us(atmp, pine_state))
		break;

	    if(!atmp)				/* check cc for others*/
	      for(atmp = env->cc; atmp; atmp = atmp->next)
		if(!address_is_us(atmp, pine_state))
		  break;

	    /*------ Ask user and possibly include all other recipients ---*/ 
	    if(atmp && (cc_ret == 'y' || (cc_ret == 0 
		 && (cc_ret=want_to(ALL_PMT,'n','x',NO_HELP,0,0))=='y'))){
		if(totalm == 1L && address_is_us(outgoing->to, pine_state)){
		    /*
		     * Special case: if we're only replying to one message
		     * and we sent it (hence in outgoing->to), what is
		     * *really* requested is a reply to the other
		     * recipients.
		     */
		    mail_free_address(&outgoing->to);
		    to_tail = &outgoing->to;

		    if(env->to)
		      *to_tail=reply_cpy_adr(pine_state,outgoing->to,env->to);

		    while(*to_tail)		/* stay on last address */
		      to_tail = &(*to_tail)->next;

		    if(env->cc)
		      *to_tail=reply_cpy_adr(pine_state,outgoing->to,env->cc);

		    while(*to_tail)		/* stay on last address */
		      to_tail = &(*to_tail)->next;
		}
		else{
		    if(env->to)
		      *cc_tail=reply_cpy_adr(pine_state,outgoing->cc,env->to);

		    while(*cc_tail)		/* stay on last address */
		      cc_tail = &(*cc_tail)->next;

		    if(env->cc)
		      *cc_tail=reply_cpy_adr(pine_state,outgoing->cc,env->cc);

		    while(*cc_tail)		/* stay on last address */
		      cc_tail = &(*cc_tail)->next;
		}
	    }

	    if(cc_ret == 'x') {
		q_status_message(1, 0, 3, "Reply cancelled");
		goto done_early;
	    }
	}

	/* get fcc */
	if(outgoing->to){
	    ADDRESS *addr;
	    char    *to;

	    /* pick off the first addr in list */
	    addr = copyaddr(outgoing->to);
	    to   = cpystr(addr_string(addr));
	    if(addr)
		mail_free_address(&addr);

	    (void)build_address(to, NULL, NULL, &fcc);

	    if(to)
		fs_give((void **)&to);
	}

	/*------------ Format the subject line ---------------*/
	if(outgoing->subject){
	    /*
	     * if reply to more than one message, and all subjects
	     * match, so be it.  otherwise set it to something generic...
	     */
	    if(strucmp(outgoing->subject,
		       reply_subject(env->subject, tmp_20k_buf))){
		fs_give((void **)&outgoing->subject);
		outgoing->subject = cpystr("Re: several messages");
	    }
	}
	else
	  outgoing->subject = reply_subject(env->subject, NULL);
    }

    seq[++times] = -1L;		/* mark end of sequence list */

    /*==========  Other miscelaneous fields ===================*/   
    outgoing->return_path = (ADDRESS *)NULL;
    outgoing->bcc         = (ADDRESS *)NULL;
    outgoing->sender      = (ADDRESS *)NULL;
    outgoing->return_path = (ADDRESS *)NULL;
    outgoing->in_reply_to = generate_in_reply_to(env);
    outgoing->remail      = NULL;
    outgoing->reply_to    = (ADDRESS *)NULL;

    outgoing->message_id  = generate_message_id(pine_state);

    if(!outgoing->to &&
       !outgoing->cc &&
       !outgoing->bcc &&
       !outgoing->newsgroups)
      q_status_message(0, 4, 6,
		       "\007Warning: no valid addresses to reply to!");

#if	defined(DOS) && !defined(_WINDOWS)
#if	defined(LWP) || defined(PCTCP) || defined(PCNFS)
#define	IN_RESERVE	8192
#else
#define	IN_RESERVE	16384
#endif
    if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
        q_status_message(1, 2, 4,"\007Insufficient memory for message text");
	goto done_early;
    }
#endif

   /*==================== Now fix up the message body ====================*/

    /* 
     * create storage object to be used for message text
     */
    if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
        q_status_message(1, 2, 4,"\007Error allocating message text");
        goto done_early;
    }

    gf_set_so_writec(&pc, (STORE_S *)msgtext);

    /*---- Include the original text if requested ----*/
    if(include_text){
	/* write preliminary envelope info into message text */
	if(F_OFF(F_SIG_AT_BOTTOM, pine_state)){
	    if(sig[0]){
		so_puts((STORE_S *)msgtext, sig);
		fs_give((void **)&sig);
	    }
	    else
	      so_puts((STORE_S *)msgtext, NEWLINE);

	    so_puts((STORE_S *)msgtext, NEWLINE);
	}

	if(totalm > 1L){
	    body                  = mail_newbody();
	    body->type            = TYPETEXT;
	    body->contents.binary = msgtext;
	    env			  = NULL;

	    for(msgno = mn_first_cur(pine_state->msgmap);
		msgno > 0L;
		msgno = mn_next_cur(pine_state->msgmap)){

		if(env){			/* put 2 between messages */
		    gf_puts(NEWLINE, pc);
		    gf_puts(NEWLINE, pc);
		}

		/*--- Grab current envelope ---*/
		env = mail_fetchstructure(pine_state->mail_stream,
					  mn_m2raw(pine_state->msgmap, msgno),
					  &orig_body);
		if(!env || !orig_body){
		    q_status_message1(1,2,4,
			      "Error fetching message %s. Can't reply to it.",
			      long2string(mn_get_cur(pine_state->msgmap)));
		    goto done_early;
		}

		if(orig_body == NULL || orig_body->type == TYPETEXT) {
		    reply_delimiter(env, pc);
		    if(F_ON(F_INCLUDE_HEADER, pine_state)){
			if(!full_text_header(pine_state->mail_stream,
					   mn_m2raw(pine_state->msgmap, msgno),
					   pc, prefix))
			  format_text_header(env, pc, prefix);
		    }

		    get_body_part_text(pine_state->mail_stream, orig_body,
				       mn_m2raw(pine_state->msgmap, msgno),
				       "1", pc, prefix, ". Text not included");
		} else if(orig_body->type == TYPEMULTIPART) {
		    if(!warned++)
		      q_status_message(0,2,3,
		      "WARNING!  Attachments not included in multiple reply.");

		    if(orig_body->contents.part &&
		       orig_body->contents.part->body.type == TYPETEXT) {
			/*---- First part of the message is text -----*/
			reply_delimiter(env, pc);
			if(F_ON(F_INCLUDE_HEADER, pine_state)){
			    if(!full_text_header(pine_state->mail_stream,
					    mn_m2raw(pine_state->msgmap,msgno),
					    pc, prefix))
			      format_text_header(env, pc, prefix);
			}

			get_body_part_text(pine_state->mail_stream,
					   &orig_body->contents.part->body,
					   mn_m2raw(pine_state->msgmap, msgno),
					   "1", pc, prefix,
					   ". Text not included");
		    } else {
			q_status_message(0,1,3,
				     "Multipart with no leading text part!");
		    }
		} else {
		    /*---- Single non-text message of some sort ----*/
		    q_status_message(0,1,3,
				     "Non-text message not included!");
		}
	    }
	}
	else{
	    msgno = mn_m2raw(pine_state->msgmap,
			     mn_get_cur(pine_state->msgmap));

	    /*--- Grab current envelope ---*/
	    env = mail_fetchstructure(pine_state->mail_stream, msgno,
				      &orig_body);
	    if(!env || !orig_body) {
		q_status_message1(1,2,4,
			      "Error fetching message %s. Can't reply to it.",
			      long2string(mn_get_cur(pine_state->msgmap)));
		goto done_early;
	    }

	    if(orig_body == NULL || orig_body->type == TYPETEXT
	       || F_OFF(F_ATTACHMENTS_IN_REPLY, pine_state)) {

		/*------ Simple text-only message ----*/
		body                  = mail_newbody();
		body->type            = TYPETEXT;
		body->contents.binary = msgtext;
		reply_delimiter(env, pc);
		if(F_ON(F_INCLUDE_HEADER, pine_state)){
		    if(!full_text_header(pine_state->mail_stream, msgno,
					 pc, prefix))
		      format_text_header(env, pc, prefix);
		}

		get_body_part_text(pine_state->mail_stream, orig_body, msgno,
				   "1", pc, prefix, ". Text not included");
	    } else if(orig_body->type == TYPEMULTIPART) {

		/*------ Message is Multipart ------*/
/* BUG: need to do a better job for MULTIPART/alternate --*/
		body = copy_body(NULL, orig_body);

		if(orig_body->contents.part != NULL &&
		   orig_body->contents.part->body.type == TYPETEXT) {
		    /*---- First part of the message is text -----*/
		    body->contents.part->body.contents.binary = msgtext;
/* BUG: ? matter that we're not setting body.size.bytes */
		    reply_delimiter(env, pc);
		    if(F_ON(F_INCLUDE_HEADER, pine_state)){
			if(!full_text_header(pine_state->mail_stream, msgno,
					     pc, prefix))
			  format_text_header(env, pc, prefix);
		    }

		    get_body_part_text(pine_state->mail_stream, 
				       &orig_body->contents.part->body,
				       msgno, "1", pc, prefix,
				       ". Text not included");
		    if(!fetch_contents(pine_state->mail_stream,msgno,body,
				       body))
		      q_status_message(1, 2, 4,
                                  "\007Error including all message parts");
		} else {
		    /*--- Fetch the original pieces ---*/
		    if(!fetch_contents(pine_state->mail_stream, msgno, body,
				       body))
		      q_status_message(1, 2, 4,
                                   "\007Error including all message parts");

		    /*--- No text part, create a blank one ---*/
		    part                       = mail_newbody_part();
		    part->next                 = body->contents.part;
		    body->contents.part        = part;
		    part->body.contents.binary = msgtext;
/* BUG: ? matter that we're not setting body.size.bytes */
		}
	    } else {
		/*---- Single non-text message of some sort ----*/
		body                = mail_newbody();
		body->type          = TYPEMULTIPART;
		part                = mail_newbody_part();
		body->contents.part = part;
    
		/*--- The first part, a blank text part to be edited ---*/
		part->body.type            = TYPETEXT;
		part->body.contents.binary = msgtext;
/* BUG: ? matter that we're not setting body.size.bytes */

		/*--- The second part, what ever it is ---*/
		part->next           = mail_newbody_part();
		part                 = part->next;
		part->body.id
				     = generate_message_id(pine_state);
		copy_body(&(part->body), orig_body);
		/*
		 * the idea here is to fetch part into storage object
		 */
		if(part->body.contents.binary = (void *) so_get(PART_SO_TYPE,
							    NULL,EDIT_ACCESS)){
#ifdef	DOS
		    mail_parameters(ps_global->mail_stream, SET_GETS,
				    (void *)dos_gets); /* fetched to disk */
		    append_file = (FILE *)so_text(
					(STORE_S *)part->body.contents.binary);

		    if(mail_fetchbody(pine_state->mail_stream, msgno, "1",
				      &part->body.size.bytes) == NULL)
		      goto done;

		    mail_parameters(ps_global->mail_stream, SET_GETS,
				    (void *)NULL);
		    append_file = NULL;
		    mail_gc(pine_state->mail_stream, GC_TEXTS);
#else
		    if((tp=mail_fetchbody(pine_state->mail_stream, msgno, "1",
					  &part->body.size.bytes)) == NULL)
		      goto done;
		    so_puts((STORE_S *)part->body.contents.binary, tp);
#endif
		}
		else
		  goto done;
	    }
	}
    } else {
        /*--------- No text included --------*/
        body                  = mail_newbody();
        body->type            = TYPETEXT;
	body->contents.binary = msgtext;
    }

    if(sig)
      so_puts(msgtext, sig);

#if	defined(DOS) && !defined(_WINDOWS)
    free((void *)reserve);
#endif

    /* partially formatted outgoing message */
    pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY", fcc, seq, refs, NULL,0);

  done:
    pine_free_body(&body);
  done_early:
    mail_free_envelope(&outgoing);
    fs_give((void **)&seq);
    fs_give((void **)&refs);
    if(fcc)
      fs_give((void **)&fcc);
    if(sig)
      fs_give((void **)&sig);
}



/*----------------------------------------------------------------------
    Fetch and write the message's full header if we're supposed to...

Args:  stream   -- stream associated with folder containing message
       msgno -- message to write
       pc -- function to write with
       prefix -- string to insert before each header line

  ---*/
int
full_text_header(stream, msgno, pc, prefix)
    MAILSTREAM *stream;
    long	msgno;
    gf_io_t	pc;
    char       *prefix;
{
    int   rv = 0;
    char *h;

    if(!ps_global->full_header || !stream)
      return(0);

    if(h = mail_fetchheader(stream, msgno)){
	q_status_message(0, 1, 3,
		       "Full header mode ON.  All header text being included");
	if(prefix && *prefix && !gf_puts(prefix, pc))
	  return(0);

	rv = 1;
	for(; *h; h++){
	    if(!(*pc)(*h))
	      return(0);		/* BUG: wouldn't an error be better? */

	    if(prefix && *prefix && *h == '\015' && *(h+1) == '\012' && *(h+2)
	       && (!(*pc)(*++h) || !gf_puts(prefix, pc)))
	      return(0);		/* BUG: wouldn't an error be better? */
	}
    }
    else
      gf_puts("[ Error fetching header. ]", pc);

    return(rv);
}



/*----------------------------------------------------------------------
    Copy the given address list to the destination address list pointer
  filtering out those already in the "mask" list and ourself.

Args:  mask   --
       source --

  ---*/
ADDRESS *
reply_cpy_adr(ps, mask, source)
     struct pine *ps;
     ADDRESS     *mask, *source;
{
    ADDRESS *tmp, *ret = NULL, **ret_tail;

    ret_tail = &ret;
    for(source = first_addr(source); source; source = source->next){
	for(tmp = first_addr(mask); tmp; tmp = tmp->next)
	  if(address_is_same(source, tmp))
	    break;

	/*
	 * If there's no match in mask *and* this address isn't us, copy...
	 */
	if(!tmp && (!ps || !address_is_us(source, ps))){
	    tmp          = source->next;
	    source->next = NULL;	/* only copy one addr! */
	    *ret_tail    = rfc822_cpy_adr(source);
	    ret_tail     = &(*ret_tail)->next;
	    source->next = tmp;		/* restore rest of list */
	}
    }

    return(ret);
}



/*----------------------------------------------------------------------
    Test the given address lists for equivalence

Args:  x -- First address list for comparison
       y -- Second address for comparison

  ---*/
int
reply_match_adrs(x, y)
     ADDRESS *x, *y;
{
    for(x = first_addr(x), y = first_addr(y); x && y; x = x->next, y = y->next)
      if(!address_is_same(x, y))
	return(0);

    return(!x && !y);			/* true if ran off both lists */
}



/*----------------------------------------------------------------------
    Format and return subject suitable for the reply command

Args:  subject -- subject to build reply subject for
       buf -- buffer to use for writing.  If non supplied, alloc one.

Returns: with either "Re:" prepended or not, if already there.
         returned string is allocated.
  ---*/
char *
reply_subject(subject, buf)
     char *subject;
     char *buf;
{
    if(!buf)
      buf = fs_get(((subject && *subject) ? strlen(subject) : 10) + 5);

    if(subject					/* already "re:" ? */
       && (subject[0] == 'R' || subject[0] == 'r')
       && (subject[1] == 'E' || subject[1] == 'e')
       &&  subject[2] == ':')
      strcpy(buf, subject);
    else
      sprintf(buf, "Re: %s", (subject && *subject) ? subject : "your mail");

    return(buf);
}



/*----------------------------------------------------------------------
    Format the header of the text for inclusion in a reply or forward

Args:  env       -- Envelope, must not be NULL
       pc        -- function used to write each header character
       prefix    -- string to be placed before each line

  ---*/
void
format_text_header(env, pc, prefix)
    ENVELOPE *env;
    gf_io_t   pc;
    char     *prefix;
{
    char    *pfx = (prefix) ? prefix : "";
    char    *p;

    if(!env)
      return;

    /*---- The Date: field -----*/
    if(env->date){
	gf_puts(pfx, pc);
	gf_puts("Date: ", pc);
	gf_puts(env->date, pc);
	gf_puts(NEWLINE, pc);
    }

    /*---- The From: field ----*/
    gf_puts(pfx, pc);
    gf_puts("From: ", pc);
    if (!env->from) {
	if(!ps_global->anonymous)
	  gf_puts("    (Sender of message unknown)", pc);
    }
    else{
	if(env->from->personal) {
	    gf_puts(env->from->personal, pc);
	    gf_puts(" <", pc);
	}

	gf_puts(env->from->mailbox, pc);
	if(env->from->host){
	    (*pc)('@');
	    gf_puts(env->from->host, pc);
	}

	if(env->from->personal)
	  (*pc)('>');
    }

    gf_puts(NEWLINE, pc);

    /*-------- The To: field --------*/
    if(env->to)
      pretty_addr_string("To: ",env->to, pfx, pc);

    /*-------- The Cc: field -----*/
    if(env->cc)
      pretty_addr_string("Cc: ",env->cc, pfx, pc);

    /*-------- The Newsgroups: field -----*/
    if(env->newsgroups)
      pretty_newsgroup_string("Newgroups: ",env->newsgroups, pfx, pc);    

    /*-------- The subject line ----*/
    if(env->subject){
	gf_puts(pfx, pc);
	gf_puts("Subject: ", pc);
	gf_puts(env->subject, pc);
	gf_puts(NEWLINE, pc);
    }

    gf_puts(pfx, pc);		/* prefix followed by */
    gf_puts(NEWLINE, pc);	/* blank separater!   */
}


/*
 * reply_delimeter - output formatted reply delimiter for given envelope
 *		     with supplied character writing function.
 */
void
reply_delimiter(env, pc)
    ENVELOPE *env;
    gf_io_t   pc;
{
    struct date d;

    parse_date(env->date, &d);
    gf_puts("On ", pc);				/* All delims have... */
    if(d.wkday != -1){				/* "On day, date month year" */
	gf_puts(week_abbrev(d.wkday), pc);	/* in common */
	gf_puts(", ", pc);
    }

    gf_puts(int2string(d.day), pc);
    (*pc)(' ');
    gf_puts(month_abbrev(d.month), pc);
    (*pc)(' ');
    gf_puts(int2string(d.year), pc);

    if(!env->from) {				/* but what follows, depends */
	gf_puts(", it was written:", pc);
    }
    else if (env->from->personal) {
	gf_puts(", ", pc);
	gf_puts(env->from->personal, pc);
	gf_puts(" wrote:", pc);
    }
    else {
	(*pc)(' ');
	gf_puts(env->from->mailbox, pc);
	if(env->from->host){
	    (*pc)('@');
	    gf_puts(env->from->host, pc);
	}

	gf_puts(" wrote:", pc);
    }

    gf_puts(NEWLINE, pc);			/* and end with two newlines */
    gf_puts(NEWLINE, pc);
}


/*
 * forward_delimiter - return delimiter for forwarded text
 */
void
forward_delimiter(pc)
    gf_io_t pc;
{
    gf_puts(NEWLINE, pc);
    gf_puts("---------- Forwarded message ----------", pc);
    gf_puts(NEWLINE, pc);
}


/*----------------------------------------------------------------------
       Partially set up message to forward and pass off to composer/mailer

    Args: pine_state -- The usual pine structure

  Result: outgoing envelope and body created and passed off to composer/mailer

   Create the outgoing envelope for the mail being forwarded, which is 
not much more than filling in the subject, and create the message body
of the outgoing message which requires formatting the header from the
envelope of the original messasge.
  ----------------------------------------------------------------------*/
void
forward(pine_state)
    struct pine   *pine_state;
{
    ENVELOPE      *env, *outgoing;
    BODY          *body, *orig_body, *text_body, *b;
    char          *tmp_text, *sig;
    long           msgno, totalmsgs;
    PART          *part;
    MAILSTREAM    *stream;
    void          *msgtext;
    gf_io_t        pc;
    int            ret;
#if	defined(DOS) && !defined(_WINDOWS)
    char	  *reserve;
#endif

    dprint(4, (debugfile, "\n - forward -\n"));

    stream                = pine_state->mail_stream;
    outgoing              = mail_newenvelope();
    outgoing->message_id  = generate_message_id(pine_state);

    if((totalmsgs = mn_total_cur(pine_state->msgmap)) > 1L){
	sprintf(tmp_20k_buf, "%s forwarded messages...", comatose(totalmsgs));
	outgoing->subject = cpystr(tmp_20k_buf);
    }
    else{
	/*---------- Get the envelope of message we're forwarding ------*/
	msgno = mn_m2raw(pine_state->msgmap, mn_get_cur(pine_state->msgmap));
	if(!(outgoing->subject = forward_subject(pine_state, msgno))){
	    q_status_message1(1,2,4,
			      "Error fetching message %s. Can't forward it.",
			      long2string(msgno));
	    goto clean_early;
	}
    }

    /*
     * as with all text bound for the composer, build it in 
     * a storage object of the type it understands...
     */
    if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
	q_status_message(1, 2, 4,"\007Error allocating message text");
	goto clean_early;
    }

    
    so_puts((STORE_S *)msgtext, *(sig = get_signature()) ? sig : NEWLINE);
    fs_give((void **)&sig);
    gf_set_so_writec(&pc, (STORE_S *)msgtext);

#if	defined(DOS) && !defined(_WINDOWS)
#if	defined(LWP) || defined(PCTCP) || defined(PCNFS)
#define	IN_RESERVE	8192
#else
#define	IN_RESERVE	16384
#endif
    if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
	so_give(&(STORE_S *)msgtext);
        q_status_message(1, 2, 4,"\007Insufficient memory for message text");
	goto clean_early;
    }
#endif

    ret = (totalmsgs > 1L)
	   ? want_to("Forward messages as a MIME digest",'y','x',NO_HELP,0,0)
	   : (pine_state->full_header)
	     ? want_to("Forward message as an attachment",'n','x',NO_HELP,0,0)
	     : 0;

    /*
     * If we're forwarding multiple messages *or* the forward-as-mime
     * is turned on and the users wants it done that way, package things
     * up...
     */
    if(ret == 'x'){
	q_status_message(1, 0, 3, "Forward message cancelled");
	goto clean_early;
    }
    else if(ret == 'y'){			/* attach message[s]!!! */
	PART **pp;
	long   totalsize = 0L;

	/*---- New Body to start with ----*/
        body	       = mail_newbody();
        body->type     = TYPEMULTIPART;

        /*---- The TEXT part/body ----*/
        body->contents.part                       = mail_newbody_part();
        body->contents.part->body.type            = TYPETEXT;
        body->contents.part->body.contents.binary = msgtext;

	if(totalmsgs > 1L){
	    /*---- The MULTIPART/DIGEST part ----*/
	    body->contents.part->next			= mail_newbody_part();
	    body->contents.part->next->body.type	= TYPEMULTIPART;
	    body->contents.part->next->body.subtype	= cpystr("Digest");
	    sprintf(tmp_20k_buf, "Digest of %s messages", comatose(totalmsgs));
	    body->contents.part->next->body.description = cpystr(tmp_20k_buf);
	    pp = &(body->contents.part->next->body.contents.part);
	}
	else
	  pp = &(body->contents.part->next);

	/*---- The Message body subparts ----*/
	for(msgno = mn_first_cur(pine_state->msgmap);
	    msgno > 0L;
	    msgno = mn_next_cur(pine_state->msgmap)){
	    msgno		 = mn_m2raw(pine_state->msgmap, msgno);
	    *pp			 = mail_newbody_part();
	    b			 = &((*pp)->body);
	    pp			 = &((*pp)->next);
	    b->type		 = TYPEMESSAGE;
	    b->id		 = generate_message_id(pine_state);
	    b->description       = forward_subject(pine_state, msgno);
	    b->contents.msg.env  = NULL;
	    b->contents.msg.body = NULL;

	    /*---- Package each message in a storage object ----*/
	    if((b->contents.binary = (void *) so_get(PART_SO_TYPE, NULL,
						     EDIT_ACCESS)) == NULL)
	      goto bomb;

	    /* write the header */
	    if((tmp_text = mail_fetchheader(stream, msgno)) && *tmp_text)
	      so_puts((STORE_S *)b->contents.binary, tmp_text);
	    else
	      goto bomb;

#ifdef	DOS
	    /* write fetched text to disk */
	    mail_parameters(stream, SET_GETS, (void *)dos_gets);
	    append_file = (FILE *)so_text((STORE_S *)b->contents.binary);

	    /* HACK!  See mailview.c:format_message for details... */
	    stream->text = NULL;
	    /* write the body */
	    if(mail_fetchtext(stream, msgno) == NULL)
	      goto bomb;

	    b->size.bytes = ftell(append_file);
	    /* next time body may stay in core */
	    mail_parameters(stream, SET_GETS, (void *)NULL);
	    append_file   = NULL;
	    mail_gc(stream, GC_TEXTS);
	    so_release((STORE_S *)b->contents.binary);
#else
	    b->size.bytes = strlen(tmp_text);
	    so_puts((STORE_S *)b->contents.binary, "\015\012");
	    if((tmp_text = mail_fetchtext(stream, msgno)) &&  *tmp_text)
	      so_puts((STORE_S *)b->contents.binary, tmp_text);
	    else
	      goto bomb;

	    b->size.bytes += strlen(tmp_text);
#endif
	    totalsize += b->size.bytes;
	}

	if(totalmsgs > 1L)
	  body->contents.part->next->body.size.bytes = totalsize;

    }
    else if(totalmsgs > 1L){
	int		        warned = 0;
	body                  = mail_newbody();
	body->type            = TYPETEXT;
	body->contents.binary = msgtext;
	env		      = NULL;

	for(msgno = mn_first_cur(pine_state->msgmap);
	    msgno > 0L;
	    msgno = mn_next_cur(pine_state->msgmap)){

	    if(env){			/* put 2 between messages */
		gf_puts(NEWLINE, pc);
		gf_puts(NEWLINE, pc);
	    }

	    /*--- Grab current envelope ---*/
	    env = mail_fetchstructure(pine_state->mail_stream,
				      mn_m2raw(pine_state->msgmap, msgno),
				      &orig_body);
	    if(!env || !orig_body){
		q_status_message1(1,2,4,
			       "Error fetching message %s. Can't forward it.",
			       long2string(msgno));
		goto bomb;
	    }

	    if(orig_body == NULL || orig_body->type == TYPETEXT) {
		if(!pine_state->anonymous){
		    forward_delimiter(pc);
		    if(!full_text_header(pine_state->mail_stream,
					 mn_m2raw(pine_state->msgmap, msgno),
					 pc, ""))
		      format_text_header(env, pc, "");
		}

		get_body_part_text(pine_state->mail_stream, orig_body,
				   mn_m2raw(pine_state->msgmap, msgno),
				   "1", pc, "", ". Text not included");
	    } else if(orig_body->type == TYPEMULTIPART) {
		if(!warned++)
		  q_status_message(0,2,3,
		    "WARNING!  Attachments not included in multiple forward.");

		if(orig_body->contents.part &&
		   orig_body->contents.part->body.type == TYPETEXT) {
		    /*---- First part of the message is text -----*/
		    forward_delimiter(pc);
		    if(!full_text_header(pine_state->mail_stream,
					 mn_m2raw(pine_state->msgmap,msgno),
					 pc, ""))
		      format_text_header(env, pc, "");

		    get_body_part_text(pine_state->mail_stream,
				       &orig_body->contents.part->body,
				       mn_m2raw(pine_state->msgmap, msgno),
				       "1", pc, "", ". Text not included");
		} else {
		    q_status_message(0,1,3,
				     "Multipart with no leading text part!");
		}
	    } else {
		/*---- Single non-text message of some sort ----*/
		q_status_message(0,1,3,
				 "Non-text message not included!");
	    }
	}
    }
    else {
	env = mail_fetchstructure(pine_state->mail_stream, msgno, &orig_body);
	if(!env || !orig_body){
	    q_status_message1(1,2,4,
			      "Error fetching message %s. Can't forward it.",
			      long2string(msgno));
	    goto clean_early;
	}

        if(orig_body == NULL || orig_body->type == TYPETEXT) {
            /*---- Message has a single text part -----*/
            body                  = mail_newbody();
            body->type            = TYPETEXT;
            body->contents.binary = msgtext;
    	    if(!pine_state->anonymous){
		forward_delimiter(pc);
		if(!full_text_header(pine_state->mail_stream, msgno, pc, ""))
		  format_text_header(env, pc, "");
	    }

	    get_body_part_text(pine_state->mail_stream, orig_body,
			       msgno, "1", pc, "", ". Text not included");
/* BUG: ? matter that we're not setting body.size.bytes */
        } else if(orig_body->type == TYPEMULTIPART) {
            /*---- Message is multipart ----*/
    
            /*--- Copy the body and entire structure  ---*/
            body = copy_body(NULL, orig_body);
    
            /*--- The text part of the message ---*/
            if(orig_body->contents.part->body.type == TYPETEXT) {
                /*--- The first part is text ----*/
                text_body                  = &body->contents.part->body;
		text_body->contents.binary = msgtext;
    		if(!pine_state->anonymous){
		    forward_delimiter(pc);
		    if(!full_text_header(pine_state->mail_stream, msgno,pc,""))
		      format_text_header(env, pc, "");
		}

		get_body_part_text(pine_state->mail_stream, 
				   &orig_body->contents.part->body,
				   msgno, "1", pc, "", ". Text not included");
/* BUG: ? matter that we're not setting body.size.bytes */
                if(!fetch_contents(stream, msgno, body, body))
                  goto bomb;
            } else {
		if(!fetch_contents(stream, msgno, body, body))
		  goto bomb;

                /*--- Create a new blank text part ---*/
                part                       = mail_newbody_part();
                part->next                 = body->contents.part;
                body->contents.part        = part;
                part->body.contents.binary = msgtext;
            }
    
        } else {
            /*---- A single part message, not of type text ----*/
            body                     = mail_newbody();
            body->type               = TYPEMULTIPART;
            part                     = mail_newbody_part();
            body->contents.part      = part;
    
            /*--- The first part, a blank text part to be edited ---*/
            part->body.type            = TYPETEXT;
            part->body.contents.binary = msgtext;
    
            /*--- The second part, what ever it is ---*/
            part->next               = mail_newbody_part();
            part                     = part->next;
            part->body.id            = generate_message_id(pine_state);
            copy_body(&(part->body), orig_body);
	    /*
	     * the idea here is to fetch part into storage object
	     */
	    if(part->body.contents.binary = (void *) so_get(PART_SO_TYPE, NULL,
							    EDIT_ACCESS)){
#ifdef	DOS
		/* fetched text to disk */
		mail_parameters(stream, SET_GETS, (void *)dos_gets);
		append_file = (FILE *)so_text(
				       (STORE_S *)part->body.contents.binary);

		if(mail_fetchbody(stream, msgno, "1",
					&part->body.size.bytes) == NULL)
		  goto bomb;

		/* next time body may stay in core */
		mail_parameters(stream, SET_GETS, (void *)NULL);
		append_file = NULL;
		mail_gc(stream, GC_TEXTS);
#else
		if(tmp_text = mail_fetchbody(stream, msgno, "1",
					  &part->body.size.bytes))
		  so_puts((STORE_S *)part->body.contents.binary, tmp_text);
		else
		  goto bomb;
#endif
	    }
	    else
	      goto bomb;
        }
    }

#if	defined(DOS) && !defined(_WINDOWS)
    free((void *)reserve);
#endif
    if(pine_state->anonymous)
      pine_simple_send(outgoing, &body);
    else			/* partially formatted outgoing message */
      pine_send(outgoing, &body,
		pine_state->nr_mode ? "SEND MESSAGE" : "FORWARD MESSAGE",
		NULL, NULL, NULL, NULL, 0);

  clean:
    pine_free_body(&body);
  clean_early:
    mail_free_envelope(&outgoing);
    return;

  bomb:
#ifdef	DOS
    mail_parameters(stream, SET_GETS, (void *)NULL);
    append_file = NULL;
    mail_gc(pine_state->mail_stream, GC_TEXTS);
#endif
    q_status_message(1, 4, 5,
           "\007Error fetching message contents. Can't forward message");
    goto clean;

}



/*----------------------------------------------------------------------
  Build the subject for the message number being forwarded

    Args: pine_state -- The usual pine structure
          msgno      -- The message number to build subject for

  Result: malloc'd string containing new subject or NULL on error

  ----------------------------------------------------------------------*/
char *
forward_subject(pine_state, msgno)
     struct pine *pine_state;
     long	  msgno;
{
    ENVELOPE *env;
    int       l;

    if(!(env = mail_fetchstructure(pine_state->mail_stream, msgno, NULL)))
      return(NULL);

    dprint(9, (debugfile, "checking subject: \"%s\"\n",
	       env->subject ? env->subject : "NULL"));

    tmp_20k_buf[0] = '\0';
    if(env->subject && env->subject[0]){ /* add (fwd)? */
	strcpy(tmp_20k_buf, env->subject);
	removing_trailing_white_space(tmp_20k_buf);
	if((l=strlen(tmp_20k_buf)) < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))
	  strcat(tmp_20k_buf, " (fwd)");
    }

    return(cpystr((tmp_20k_buf[0]) ? tmp_20k_buf : "Forwarded mail...."));
}



/*----------------------------------------------------------------------
       Partially set up message to forward and pass off to composer/mailer

    Args: pine_state -- The usual pine structure
          message    -- The MESSAGECACHE of entry to reply to 

  Result: outgoing envelope and body created and passed off to composer/mailer

   Create the outgoing envelope for the mail being forwarded, which is 
not much more than filling in the subject, and create the message body
of the outgoing message which requires formatting the header from the
envelope of the original messasge.
  ----------------------------------------------------------------------*/
void
forward_text(pine_state, text, source)
     struct pine *pine_state;
     void        *text;
     SourceType   source;
{
    ENVELOPE *env;
    BODY     *body;
    gf_io_t   pc, gc;
    STORE_S  *msgtext;
    char     *enc_error, *sig;

    if(msgtext = so_get(PicoText, NULL, EDIT_ACCESS)){
	env                   = mail_newenvelope();
	env->message_id       = generate_message_id(pine_state);
	body                  = mail_newbody();
	body->type            = TYPETEXT;
	body->contents.binary = (void *) msgtext;

	if(!pine_state->anonymous){
	    so_puts(msgtext, *(sig = get_signature()) ? sig : NEWLINE);
	    so_puts(msgtext, NEWLINE);
	    so_puts(msgtext, "----- Included text -----");
	    so_puts(msgtext, NEWLINE);
	    fs_give((void **)&sig);
	}

	gf_filter_init();
	gf_set_so_writec(&pc, msgtext);
	gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L,
		     source);

	if((enc_error = gf_pipe(gc, pc)) == NULL){
	    if(pine_state->anonymous){
		pine_simple_send(env, &body);
		pine_state->mangled_footer = 1;
	    }
	    else{
		pine_send(env, &body, "SEND MESSAGE",NULL,NULL,NULL,NULL,0);
		pine_state->mangled_screen = 1;
	    }
	}
	else{
	    q_status_message1(1,3,4,"\007Error reading text \"%s\"",enc_error);
	    display_message('x');
	}

	mail_free_envelope(&env);
	pine_free_body(&body);
    }
    else {
	q_status_message(1, 2, 4, "\007Error allocating message text");
	display_message('x');
    }
}



/*----------------------------------------------------------------------
       Partially set up message to resend and pass off to mailer

    Args: pine_state -- The usual pine structure

  Result: outgoing envelope and body created and passed off to mailer

   Create the outgoing envelope for the mail being resent, which is 
not much more than filling in the subject, and create the message body
of the outgoing message which requires formatting the header from the
envelope of the original messasge.
  ----------------------------------------------------------------------*/
void
bounce(pine_state)
    struct pine   *pine_state;
{
    ENVELOPE      *env, *outgoing;
    BODY          *body, *orig_body, *text_body, *b;
    long           msgno;
    PART          *part;
    MAILSTREAM    *stream;
    void          *msgtext;
    gf_io_t        pc;
    char          *sig, *fields[2];
    int            ret=0;

    dprint(4, (debugfile, "\n - bounce -\n"));

    stream = pine_state->mail_stream;

    /*---------- Get the envelope of message we're replying to ------*/
    if(mn_total_cur(pine_state->msgmap) > 1L){
	q_status_message1(1,2,4,
			 "Can't bounce %s messages at once yet!", 
			      long2string(mn_total_cur(pine_state->msgmap)));
	return;
    }
    else{
	msgno = mn_m2raw(pine_state->msgmap, mn_get_cur(pine_state->msgmap));
	env   = mail_fetchstructure(stream, msgno, &body);
	if(!env || !body) {
	    q_status_message1(1,2,4,
			      "Error fetching message %s. Can't reply to it.",
			      long2string(msgno));
	    return;
	}
    }

    outgoing              = mail_newenvelope();
    outgoing->message_id  = generate_message_id(pine_state);


    /*==================== Subject ====================*/
    if(env->subject == NULL || strlen(env->subject) == 0)
      /* --- Blank, make up one ---*/
      outgoing->subject = cpystr("Resent mail....");

    fields[0] = "Received";
    fields[1] = NULL;
    outgoing->remail = xmail_fetchheader_lines_not(stream, msgno, fields);

    /*
     * as with all text bound for the composer, build it in 
     * a storage object of the type it understands...
     */
    if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
	q_status_message(1, 2, 4,"\007Error allocating message text");
	goto clean_early;
    }

    /*
     * Build a fake body description.  It's ignored by pine_rfc822_header,
     * but we need to set it to something that makes set_mime_types
     * not sniff it and pine_rfc822_output_body not re-encode it.
     * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
     * problems unless something tries to access body_encodings[] using
     * it without proper precautions.  We don't want to use ENCOTHER
     * cause that tells set_mime_types to sniff it, and we don't want to
     * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
     * it.  When there's time, it'd be nice to clean this interaction
     * up...
     */
    orig_body		  = body;
    body		  = mail_newbody();
    body->type		  = TYPETEXT;
    body->encoding	  = ENCMAX + 1;
    body->subtype	  = cpystr("Plain");
    body->contents.binary = msgtext ;
    gf_set_so_writec(&pc, (STORE_S *)msgtext);

    /* pass NULL body to force mail_fetchtext */
    get_body_part_text(stream, NULL, msgno, "1", pc, "",". Text not included");
    pine_simple_send(outgoing, &body);

  clean:
    pine_free_body(&body);
  clean_early:
    mail_free_envelope(&outgoing);
    return;

  bomb:
#ifdef	DOS
    mail_parameters(stream, SET_GETS, (void *)NULL);
    append_file = NULL;
    mail_gc(pine_state->mail_stream, GC_TEXTS);
#endif
    q_status_message(1, 4, 5,
		  "\007Error fetching message contents. Can't resend message");
    goto clean;

}


        
/*----------------------------------------------------------------------
  Function to report bugs to the specified ps->VAR_BUGS_ADDRESS

    Args: pine_state -- The usual pine structure

  Result: outgoing envelope and body created and passed off to mailer

  ----------------------------------------------------------------------*/
void
gripe(ps)
    struct pine *ps;
{
    BODY      *body, *pb;
    ENVELOPE  *outgoing;
    PART     **pp;
    gf_io_t    pc;
    char       tmp[MAX_ADDRESS], *p, *sig;
    int	       i, ch = 0;
    long       fake_reply = -1L,
	       msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));

    if(!ps->VAR_BUGS_ADDRESS)
      return;

    sprintf(tmp, "%s%s%s%s%s",
	    ps->VAR_BUGS_FULLNAME ? "\"" : "",
	    ps->VAR_BUGS_FULLNAME ? ps->VAR_BUGS_FULLNAME : "",
	    ps->VAR_BUGS_FULLNAME ? "\" <" : "",
	    ps->VAR_BUGS_ADDRESS,
	    ps->VAR_BUGS_FULLNAME ? ">" : "");

    dprint(1, (debugfile, "\n\n    ---- REPORTING BUG (%s) ----\n", tmp));

    outgoing   = mail_newenvelope();
    rfc822_parse_adrlist(&outgoing->to, tmp, ps->maildomain);
    outgoing->message_id = generate_message_id(ps);
    /* Build our contribution to the subject; part constant string
     * and part random 4 character alpha numeric string
     */
    sprintf(tmp_20k_buf, "Bug (ID %c%c%d%c%c): ", 
	    ((i = (int)(random() % 36L)) < 10) ? '0' + i : 'A' + (i - 10),
	    ((i = (int)(random() % 36L)) < 10) ? '0' + i : 'A' + (i - 10),
	    (int)(random() % 10L),
	    ((i = (int)(random() % 36L)) < 10) ? '0' + i : 'A' + (i - 10),
	    ((i = (int)(random() % 36L)) < 10) ? '0' + i : 'A' + (i - 10));
    outgoing->subject = cpystr(tmp_20k_buf);

    body       = mail_newbody();
    body->type = TYPEMULTIPART;

    /*---- The TEXT part/body ----*/
    body->contents.part            = mail_newbody_part();
    body->contents.part->body.type = TYPETEXT;

    /* Allocate the an object for the body */
    if(body->contents.part->body.contents.binary = 
				    (void *)so_get(PicoText,NULL,EDIT_ACCESS)){
	pp = &(body->contents.part->next);
	if((sig = get_signature()) && *sig){
	    so_puts((STORE_S *)body->contents.part->body.contents.binary, sig);
	    fs_give((void **)&sig);
	}
    }
    else {
	q_status_message(1,2,4,"\007Problem creating space for message text.");
	goto bomb;
    }

    /*---- create object, and write current config into it ----*/
    *pp			     = mail_newbody_part();
    pb			     = &((*pp)->body);
    pp			     = &((*pp)->next);
    pb->type		     = TYPETEXT;
    pb->id		     = generate_message_id(ps);
    pb->description          = cpystr("Pine Configuration Data");
    pb->parameter	     = mail_newbody_parameter();
    pb->parameter->attribute = cpystr("name");
    pb->parameter->value     = cpystr("config.txt");
    pb->contents.msg.env     = NULL;
    pb->contents.msg.body    = NULL;

    if(pb->contents.binary = (void *) so_get(CharStar, NULL, EDIT_ACCESS)){
	gf_set_so_writec(&pc, (STORE_S *)pb->contents.binary);
	dump_pine_struct(ps, pc);
	dump_config(ps, pc);
	pb->size.bytes = strlen((char *)so_text(
					      (STORE_S *)pb->contents.binary));
    }
    else{
	q_status_message(1,2,4,"\007Problem creating space for config data");
	goto bomb;
    }

    if(mn_get_total(ps->msgmap) > 0L
       && (ch = one_try_want_to(BUG_PMT,'y','x',NO_HELP,0,1)) == 'y'){
	*pp		      = mail_newbody_part();
	pb		      = &((*pp)->body);
	pb->type	      = TYPEMESSAGE;
	pb->id		      = generate_message_id(ps);
	sprintf(tmp, "Problem Message (%ld of %ld)",
		mn_get_cur(ps->msgmap), mn_get_total(ps->msgmap));
	pb->description	      = cpystr(tmp);

	/*---- Package each message in a storage object ----*/
	if(!(pb->contents.binary = (void *)so_get(PART_SO_TYPE, NULL,
						  EDIT_ACCESS))){
	    q_status_message(1,2,4,
			     "\007Problem creating space to attach message");
	    goto bomb;
	}

	/* write the header */
	if((p = mail_fetchheader(ps->mail_stream, msgno)) && *p)
	  so_puts((STORE_S *)pb->contents.binary, p);
	else
	  goto bomb;

#ifdef	DOS
	/* write fetched text to disk */
	mail_parameters(ps->mail_stream, SET_GETS, (void *)dos_gets);
	append_file = (FILE *)so_text((STORE_S *)pb->contents.binary);

	/* HACK!  See mailview.c:format_message for details... */
	ps->mail_stream->text = NULL;
	/* write the body */
	if(!mail_fetchtext(ps->mail_stream, msgno))
	  goto bomb;

	pb->size.bytes = ftell(append_file);
	/* next time body may stay in core */
	mail_parameters(ps->mail_stream, SET_GETS, (void *)NULL);
	append_file   = NULL;
	mail_gc(ps->mail_stream, GC_TEXTS);
	so_release((STORE_S *)pb->contents.binary);
#else
	pb->size.bytes = strlen(p);
	so_puts((STORE_S *)pb->contents.binary, "\015\012");
	if((p = mail_fetchtext(ps->mail_stream, msgno)) &&  *p)
	  so_puts((STORE_S *)pb->contents.binary, p);
	else
	  goto bomb;

	pb->size.bytes += strlen(p);
#endif
    }
    else if(ch == 'x'){
	q_status_message(0, 0, 3, "Bug report cancelled.");
	goto bomb;
    }

    pine_send(outgoing,&body,"COMPOSE BUG REPORT",NULL,&fake_reply,NULL,NULL,0);
    ps->mangled_screen = 1;

  bomb:
    mail_free_envelope(&outgoing);
    pine_free_body(&body);
}


        
/*----------------------------------------------------------------------
    Fetch and format text for forwarding

Args:  stream      -- Mail stream to fetch text for
       message_no  -- Message number of text for foward
       part_number -- Part number of text to forward
       env         -- Envelope of message being forwarded
       body        -- Body structure of message being forwarded

Returns:  true if OK, false if problem occured while filtering

If the text is richtext, it will be converted to plain text, since there's
no rich text editing capabilities in Pine (yet). The character sets aren't
really handled correctly here. Theoretically editing should not be allowed
if text character set doesn't match what term-character-set is set to.

It's up to calling routines to plug in signature appropriately

As with all internal text, NVT end-of-line conventions are observed.
DOESN'T sanity check the prefix given!!!
  ----*/
int
get_body_part_text(stream, body, msg_no, part_no, pc, prefix, hmm)
    MAILSTREAM *stream;
    BODY       *body;
    long        msg_no;
    char       *part_no;
    gf_io_t     pc;
    char       *prefix;
    char       *hmm; 
{
    int	      i;
    filter_t  filters[3];
    long      len;
    char     *err;
#ifdef	DOS
    char     *tmpfile_name = NULL;
#endif

    /* if null body, we must be talking to a non-IMAP2bis server.
     * No MIME parsing provided, so we just grab the message text...
     */
    if(body == NULL){
	char         *text, *decode_error;
	MESSAGECACHE *mc;
	gf_io_t       gc;
	SourceType    src = CharStar;
	int           rv = 0;

	(void)mail_fetchstructure(stream, msg_no, NULL);
	mc = mail_elt(stream,  msg_no);

#ifdef	DOS
	if(mc->rfc822_size > MAX_MSG_INCORE
	  || (ps_global->context_current->type & FTYPE_BBOARD)){
	    src = FileStar;		/* write fetched text to disk */
	    if(!(tmpfile_name = temp_nam(NULL, "pt"))
	       || !(append_file = fopen(tmpfile_name, "w+b"))){
		if(tmpfile_name)
		  fs_give((void **)&tmpfile_name);

		q_status_message1(0,2,2,"Can't build tmpfile: %s",
				  error_description(errno));
		return(rv);
	    }

	    mail_parameters(stream, SET_GETS, (void *)dos_gets);
	}
	else				/* message stays in core */
	  mail_parameters(stream, SET_GETS, (void *)NULL);
#endif

	if(text = mail_fetchtext(stream, msg_no)){
	    gf_set_readc(&gc, text,
			 (src == FileStar) ? 0L : (unsigned long)strlen(text),
			 src);
	    gf_filter_init();		/* no filters needed */
	    if(decode_error = gf_pipe(gc, pc)){
		sprintf(tmp_20k_buf, "%s%s    [Formatting error: %s]%s",
			NEWLINE, NEWLINE,
			decode_error, NEWLINE);
		gf_puts(tmp_20k_buf, pc);
		rv++;
	    }
	}
	else{
	    gf_puts(NEWLINE, pc);
	    gf_puts("    [ERROR fetching text of message]", pc);
	    gf_puts(NEWLINE, pc);
	    gf_puts(NEWLINE, pc);
	    rv++;
	}

#ifdef	DOS
	/* clean up tmp file created for dos_gets ?  If so, trash
	 * cached knowledge, and make sure next fetch stays in core
	 */
	if(src == FileStar){
	    fclose(append_file);
	    append_file = NULL;
	    unlink(tmpfile_name);
	    fs_give((void **)&tmpfile_name);
	    mail_parameters(stream, SET_GETS, (void *)NULL);
	    mail_gc(stream, GC_TEXTS);
	}
#endif
	return(rv == 0);
    }

    filters[i = 0] = NULL;

    /*
     * just use detach, but add an auxiliary filter to insert prefix,
     * and, perhaps, digest richtext
     */
    if(body->subtype != NULL && strucmp(body->subtype,"richtext") == 0){
	gf_rich2plain_opt(1);		/* set up to filter richtext */
	filters[i++] = gf_rich2plain;
    }

/* BUG: is msgno stuff working right ??? */
/* BUG: not using "hmm" !!! */
    gf_prefix_opt(prefix);
    filters[i++] = gf_prefix;
    filters[i++] = NULL;
    err = detach(stream, msg_no, body, part_no, &len, pc, filters);
    if (err != (char *)NULL)
       q_status_message2(1, 2, 4, "%s: message number %ld",err,(void *)msg_no);
    return((int)len);
}



/*----------------------------------------------------------------------
  return the c-client reference name for the given end_body part
  ----*/
char *
partno(body, end_body)
     BODY *body, *end_body;
{
    PART *part;
    int   num = 0;
    char  tmp[64], *p = NULL;

    if(body && body->type == TYPEMULTIPART) {
	part = body->contents.part;	/* first body part */

	do {				/* for each part */
	    num++;
	    if(&part->body == end_body || (p = partno(&part->body, end_body))){
		sprintf(tmp, "%d%s%s", num, (p) ? "." : "", (p) ? p : "");
		if(p)
		  fs_give((void **)&p);

		return(cpystr(tmp));
	    }
	} while (part = part->next);	/* until done */

	return(NULL);
    }
    else if(body && body->type == TYPEMESSAGE && body->subtype 
	    && !strucmp(body->subtype, "rfc822")){
	return(partno(body->contents.msg.body, end_body));
    }

    return((body == end_body) ? cpystr("1") : NULL);
}



/*----------------------------------------------------------------------
   Fill in the contents of each body part

Args: stream      -- Stream the message is on
      msgno       -- Message number the body structure is for
      root        -- Body pointer to start from
      body        -- Body pointer to fill in

Result: 1 if all went OK, 0 if there was a problem

This function copies the contents from an original message/body to
a new message/body.  It recurses down all multipart levels.

If one or more part (but not all) can't be fetched, a status message
will be queued.
 ----*/
int
fetch_contents(stream, msgno, root, body)
     MAILSTREAM *stream;
     long        msgno;
     BODY       *root, *body;
{
    char *pnum = NULL, *tp;
    int   got_one = 0;

    if(!body->id)
      body->id = generate_message_id(ps_global);
          
    if(body->type == TYPEMULTIPART){
	int   last_one = 10;		/* remember worst case */
	PART *part     = body->contents.part;

	do {
	    got_one  = fetch_contents(stream, msgno, root, &part->body);
	    last_one = min(last_one, got_one);
	}
	while(part = part->next);

	return(last_one);
    }

    if(body->contents.binary)
      return(1);			/* already taken care of... */

    pnum = partno(root, body);

    if(body->type == TYPEMESSAGE){
	body->contents.msg.env  = NULL;
	body->contents.msg.body = NULL;
	if(body->subtype && strucmp(body->subtype,"external-body")){
	    /*
	     * the idea here is to fetch everything into storage objects
	     */
	    body->contents.binary = (void *) so_get(PART_SO_TYPE, NULL,
						    EDIT_ACCESS);
#ifndef	DOS
	    if(body->contents.binary
	       && (tp = mail_fetchbody(stream,msgno,pnum,&body->size.bytes))){
		so_puts((STORE_S *)body->contents.binary, tp);
		got_one = 1;
	    }
#else
	    if(body->contents.binary){
		/* fetch text to disk */
		mail_parameters(stream, SET_GETS, (void *)dos_gets);
		append_file =(FILE *)so_text((STORE_S *)body->contents.binary);

		if(mail_fetchbody(stream, msgno, pnum, &body->size.bytes)){
		    so_release((STORE_S *)body->contents.binary);
		    got_one = 1;
		}
		else
		  q_status_message1(0,1,2,"\007Error fetching part %s",pnum);

		/* next time body may stay in core */
		mail_parameters(stream, SET_GETS, (void *)NULL);
		append_file = NULL;
		mail_gc(stream, GC_TEXTS);
	    }
#endif
	    else
	      q_status_message1(0,1,2,"\007Error fetching part %s",pnum);
	} else {
	    got_one = 1;
	}
    } else {
	/*
	 * the idea here is to fetch everything into storage objects
	 * so, grab one, then fetch the body part
	 */
	body->contents.binary = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS);
#ifndef	DOS
	if(body->contents.binary
	   && (tp=mail_fetchbody(stream, msgno, pnum, &body->size.bytes))){
	    so_puts((STORE_S *)body->contents.binary, tp);
	    got_one = 1;
	}
#else
	if(body->contents.binary){
	    /* write fetched text to disk */
	    mail_parameters(stream, SET_GETS, (void *)dos_gets);
	    append_file = (FILE *)so_text((STORE_S *)body->contents.binary);
	    if(mail_fetchbody(stream, msgno, pnum, &body->size.bytes)){
		so_release((STORE_S *)body->contents.binary);
		got_one = 1;
	    }
	    else
	      q_status_message1(0,1,2,"\007Error fetching part %s",pnum);

	    /* next time body may stay in core */
	    mail_parameters(stream, SET_GETS, (void *)NULL);
	    append_file = NULL;
	    mail_gc(stream, GC_TEXTS);
	}
#endif
	else
	  q_status_message1(0,1,2,"\007Error fetching part %s",pnum);
    }

    if(pnum)
      fs_give((void **)&pnum);

    return(got_one);

}



/*----------------------------------------------------------------------
    Copy the body structure

Args: new_body -- Pointer to already allocated body, or NULL, if none
      old_body -- The Body to copy


 This is called recursively traverses the body structure copying all the
elements. The new_body parameter can be NULL in which case a new body is
allocated. Alternatively it can point to an already allocated body
structure. This is used when copying body parts since a PART includes a 
BODY. The contents fields are *not* filled in.
  ----*/

BODY *
copy_body(new_body, old_body)
     BODY *old_body, *new_body;
{
    PART *new_part, *old_part;

    if(old_body == NULL)
      return(NULL);

    if(new_body == NULL)
      new_body = mail_newbody();

    *new_body = *old_body;
    if(old_body->id)
      new_body->id = cpystr(old_body->id);

    if(old_body->description)
      new_body->description = cpystr(old_body->description);

    if(old_body->subtype)
      new_body->subtype = cpystr(old_body->subtype);

    new_body->parameter = copy_parameters(old_body->parameter);

    new_part = NULL;
    if(new_body->type == TYPEMULTIPART) {
        for(old_part = new_body->contents.part; old_part != NULL;
            old_part = old_part->next){
            if(new_part == NULL) {
                new_part = mail_newbody_part();
                new_body->contents.part = new_part;
            } else {
                new_part->next = mail_newbody_part();
                new_part = new_part->next;
            }
            copy_body(&(new_part->body), &(old_part->body));
        }
    } else {
        new_body->contents.binary = NULL;
    }
    return(new_body);
}



/*----------------------------------------------------------------------
    Copy the MIME parameter list
 
 Allocates storage for new part, and returns pointer to new paramter
list. If old_p is NULL, NULL is returned.
 ----*/

PARAMETER *
copy_parameters(old_p)
     PARAMETER *old_p;
{
    PARAMETER *new_p, *p1, *p2;

    if(old_p == NULL)
      return((PARAMETER *)NULL);

    new_p = p2 = NULL;
    for(p1 = old_p; p1 != NULL; p1 = p1->next) {
        if(new_p == NULL) {
            p2 = mail_newbody_parameter();
            new_p = p2;
        } else {
            p2->next = mail_newbody_parameter();
            p2 = p2->next;
        }
        p2->attribute = cpystr(p1->attribute);
        p2->value     = cpystr(p1->value);
    }
    return(new_p);
}
    
    

/*----------------------------------------------------------------------
    Make a complete copy of an envelope and all it's fields

Args:    e -- the envelope to copy

Result:  returns the new envelope, or NULL, if the given envelope was NULL

  ----*/

ENVELOPE *    
copy_envelope(e)
     register ENVELOPE *e;
{
    register ENVELOPE *e2;

    if(!e)
      return(NULL);

    e2		    = mail_newenvelope();
    e2->remail      = e->remail	     ? cpystr(e->remail)	      : NULL;
    e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL;
    e2->date        = e->date	     ? cpystr(e->date)		      : NULL;
    e2->from        = e->from	     ? rfc822_cpy_adr(e->from)	      : NULL;
    e2->sender      = e->sender	     ? rfc822_cpy_adr(e->sender)      : NULL;
    e2->reply_to    = e->reply_to    ? rfc822_cpy_adr(e->reply_to)    : NULL;
    e2->subject     = e->subject     ? cpystr(e->subject)	      : NULL;
    e2->to          = e->to          ? rfc822_cpy_adr(e->to)	      : NULL;
    e2->cc          = e->cc          ? rfc822_cpy_adr(e->cc)	      : NULL;
    e2->bcc         = e->bcc         ? rfc822_cpy_adr(e->bcc)	      : NULL;
    e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to)	      : NULL;
    e2->newsgroups  = e->newsgroups  ? cpystr(e->newsgroups)	      : NULL;
    e2->message_id  = e->message_id  ? cpystr(e->message_id)	      : NULL;
    return(e2);
}


/*----------------------------------------------------------------------
     Generate the "In-reply-to" text from message header

  Args: message -- Envelope of original message

  Result: returns an alloc'd string or NULL if there is a problem
 ----*/
char *
generate_in_reply_to(env)
    ENVELOPE *env;
{
    return((env && env->message_id) ? cpystr(env->message_id) : NULL);
}


/*----------------------------------------------------------------------
        Generate a unique message id string.

   Args: ps -- The usual pine structure

  Result: Alloc'd unique string is returned

Uniqueness is gaurenteed by using the host name, process id, date to the
second and a single unique character
*----------------------------------------------------------------------*/
char *
generate_message_id(ps)
     struct pine *ps;
{
    static char a = 'A';
    char       *id;
    long        now;
    struct tm  *now_x;

    now   = time(0);
    now_x = localtime(&now);
    id    = (char *)fs_get(128 * sizeof(char));

    sprintf(id,"<Pine.%.4s.%.20s.%02d%02d%02d%02d%02d%02d.%d%c@%.50s>",
	    SYSTYPE, pine_version, now_x->tm_year, now_x->tm_mon + 1,
	    now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec,
	    getpid() & 65535, a, ps->hostname);	/*~~~ VMS - added & 65535 */

    a = (a == 'Z') ? 'a' : (a == 'z') ? 'A' : a + 1;
    return(id);
}



/*----------------------------------------------------------------------
  Return the first true address pointer (modulo group syntax allowance)

  Args: addr  -- Address list

 Result: First real address pointer, or NULL
  ----------------------------------------------------------------------*/
ADDRESS *
first_addr(addr)
    ADDRESS *addr;
{
    while(addr && !addr->host)
      addr = addr->next;

    return(addr);
}



/*----------------------------------------------------------------------
     Format an address field, wrapping lines nicely at commas

  Args: field_name  -- The name of the field we're formatting ("TO:", Cc:...)
        addr        -- ADDRESS structure to format
        line_prefix -- A prefix string for each line such as "> "

 Result: A formatted, malloced string is returned.

The resuling lines formatted are 80 columns wide.
  ----------------------------------------------------------------------*/
void
pretty_addr_string(field_name, addr, line_prefix, pc)
    ADDRESS *addr;
    char    *line_prefix, *field_name;
    gf_io_t   pc;
{
    char     buf[MAILTMPLEN];
    int	     trailing = 0, llen, alen, plen;
    ADDRESS *atmp;

    if(!addr)
      return;

    gf_puts(line_prefix, pc);
    gf_puts(field_name, pc);
    plen = strlen(line_prefix);
    llen = plen + strlen(field_name);
    while(addr){
	atmp       = addr->next; 	/* remember what's next */
	addr->next = NULL;
	buf[0]     = '\0';
	rfc822_write_address(buf, addr); /* write address into buf */
	alen = strlen(buf);
	if(!trailing){			/* first time thru, just address */
	    llen += alen;
	    trailing++;
	}
	else{				/* else preceding comma */
	    if(addr->host){		/* if not group syntax... */
		gf_puts(",", pc);
		llen++;
	    }

	    if(alen + llen + 1 > 76){
		gf_puts(NEWLINE, pc);
		gf_puts(line_prefix, pc);
		gf_puts("    ", pc);
		llen = alen + plen + 5;
	    }
	    else{
		gf_puts(" ", pc);
		llen += alen + 1;
	    }
	}

	if(alen && llen > 76){		/* handle long addresses */
	    register char *q, *p = &buf[alen-1];

	    while(p > buf){
		if(isspace(*p) && (llen - (alen - (int)(p - buf))) < 76){
		    for(q = buf; q < p; q++)
		      (*pc)(*q);	/* write character */

		    gf_puts(NEWLINE, pc);
		    gf_puts("    ", pc);
		    gf_puts(p, pc);
		    break;
		}
		else
		  p--;
	    }

	    if(p == buf)		/* no reasonable break point */
	      gf_puts(buf, pc);
	}
	else
	  gf_puts(buf, pc);

	addr->next = atmp;
	addr       = atmp;
    }

    gf_puts(NEWLINE, pc);
}

/*----------------------------------------------------------------------
     Format an address field, wrapping lines nicely at commas

  Args: field_name  -- The name of the field we're formatting ("TO:", Cc:...)
        newsgrps    -- ADDRESS structure to format
        line_prefix -- A prefix string for each line such as "> "

 Result: A formatted, malloced string is returned.

The resuling lines formatted are 80 columns wide.
  ----------------------------------------------------------------------*/
void
pretty_newsgroup_string(field_name, newsgrps, line_prefix, pc)
    char    *newsgrps;
    char    *line_prefix, *field_name;
    gf_io_t   pc;
{
    char     buf[MAILTMPLEN];
    int	     trailing = 0, llen, alen, plen;
    char    *next_ng;
    
    if(!newsgrps || !*newsgrps)
      return;

    gf_puts(line_prefix, pc);
    gf_puts(field_name, pc);
    plen = strlen(line_prefix);
    llen = plen + strlen(field_name);
    while(*newsgrps){
        for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
        strncpy(buf, newsgrps, next_ng - newsgrps);
        buf[next_ng - newsgrps] = '\0';
        newsgrps = next_ng;
        if(*newsgrps)
          newsgrps++;
	alen = strlen(buf);
	if(!trailing){			/* first time thru, just address */
	    llen += alen;
	    trailing++;
	}
	else{				/* else preceding comma */
	    gf_puts(",", pc);
	    llen++;

	    if(alen + llen + 1 > 76){
		gf_puts(NEWLINE, pc);
		gf_puts(line_prefix, pc);
		gf_puts("    ", pc);
		llen = alen + plen + 5;
	    }
	    else{
		gf_puts(" ", pc);
		llen += alen + 1;
	    }
	}

	if(alen && llen > 76){		/* handle long addresses */
	    register char *q, *p = &buf[alen-1];

	    while(p > buf){
		if(isspace(*p) && (llen - (alen - (int)(p - buf))) < 76){
		    for(q = buf; q < p; q++)
		      (*pc)(*q);	/* write character */

		    gf_puts(NEWLINE, pc);
		    gf_puts("    ", pc);
		    gf_puts(p, pc);
		    break;
		}
		else
		  p--;
	    }

	    if(p == buf)		/* no reasonable break point */
	      gf_puts(buf, pc);
	}
	else
	  gf_puts(buf, pc);
    }

    gf_puts(NEWLINE, pc);
}



/*----------------------------------------------------------------------
  Acquire the pinerc defined signature file

  ----*/
char *
get_signature()
{
    char *sig = NULL, *tmp_sig, sig_path[MAXPATH+1];

    /*----- Get the signature if there is one to get -----*/
    if(ps_global->VAR_SIGNATURE_FILE != NULL &&
       ps_global->VAR_SIGNATURE_FILE[0] != '\0') {
#ifdef	DOS
	if(ps_global->VAR_SIGNATURE_FILE[0] == '\\' 
	   || (isalpha(ps_global->VAR_SIGNATURE_FILE[0])
	       && ps_global->VAR_SIGNATURE_FILE[1] == ':')){
	    strcpy(sig_path, ps_global->VAR_SIGNATURE_FILE);
	}
	else{
	    int l = last_cmpnt(ps_global->pinerc) - ps_global->pinerc;

	    strncpy(sig_path, ps_global->pinerc, l);
	    sig_path[l] = '\0';
	    strcat(sig_path, ps_global->VAR_SIGNATURE_FILE);
	}
#else
	if(ps_global->VAR_SIGNATURE_FILE[0] == '/'){
	    strcpy(sig_path, ps_global->VAR_SIGNATURE_FILE);
	}
	else if(ps_global->VAR_SIGNATURE_FILE[0] == '~'){
	    strcpy(sig_path, ps_global->VAR_SIGNATURE_FILE);
	    fnexpand(sig_path, sizeof(sig_path));
	}
	else
	  build_path(sig_path,ps_global->home_dir,
		     ps_global->VAR_SIGNATURE_FILE);
#endif

	if(can_access(sig_path, ACCESS_EXISTS) == 0){
	    if(tmp_sig = read_file(sig_path)){
		sig = fs_get(strlen(tmp_sig) + 10);
		strcpy(sig, NEWLINE);
		strcat(sig, NEWLINE);
		strcat(sig, tmp_sig);
		fs_give((void **)&tmp_sig);
	    }
	    else
	      q_status_message2(1, 2, 4,
			    "\007Error \"%s\" reading signature file \"%s\"",
				error_description(errno), sig_path);
	}
    }

    return(sig ? sig : cpystr(""));
}
