[comp.mail.elm] Elm 2.1 PL1 part 17 of 22

syd@dsinc.UUCP (Syd Weinstein) (12/14/88)

---- Cut Here and unpack ----
#!/bin/sh
# this is part 17 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file src/reply.c continued
#
CurArch=17
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file src/reply.c"
sed 's/^X//' << 'SHAR_EOF' >> src/reply.c
X	int  return_value, form_letter;
X
X	form_letter = (header_table[current-1].status & FORM_LETTER);
X
X	get_return(return_address);
X
X	if (first_word(header_table[current-1].from, "To:")) {
X	  strcpy(subject, header_table[current-1].subject);
X	  if (form_letter)
X	    return_value = mail_filled_in_form(return_address, subject);
X	  else
X	    return_value = sendmsg(return_address, "", subject, TRUE, NO, TRUE);
X	}
X	else if (header_table[current-1].subject[0] != '\0') {
X	  if ((strncmp("Re:", header_table[current-1].subject, 3) == 0) ||
X	      (strncmp("RE:", header_table[current-1].subject, 3) == 0) ||
X	      (strncmp("re:", header_table[current-1].subject, 3) == 0)) 
X	    strcpy(subject, header_table[current-1].subject);
X	  else {
X	    strcpy(subject,"Re: ");
X	    strcat(subject,header_table[current-1].subject); 
X	  }
X	  if (form_letter)
X	    return_value = mail_filled_in_form(return_address, subject);
X	  else
X	    return_value = sendmsg(return_address, "", subject, TRUE, NO, TRUE);
X	}
X	else
X	  if (form_letter)
X	    return_value = mail_filled_in_form(return_address, 
X						"Filled in Form");
X	  else
X	    return_value = sendmsg(return_address, "", "Re: your mail", 
X				TRUE, NO, TRUE);
X
X	return(return_value);
X}
X
Xint
Xreply_to_everyone()
X{
X	/** Reply to everyone who received the current message.  
X	    This includes other people in the 'To:' line and people
X	    in the 'Cc:' line too.  Returns non-zero iff the screen 
X            has to be rewritten. **/
X
X	char return_address[LONG_SLEN], subject[SLEN];
X	char full_address[VERY_LONG_STRING];
X	int  return_value;
X
X	get_return(return_address);
X
X#ifndef HAVE_GROUP_REPLIES_BE_A_CC
X	full_address[0] = '\0';			/* no copies yet    */
X#else
X	strcpy(full_address, return_address);	/* sender gets copy */
X#endif
X	
X	get_and_expand_everyone(return_address, full_address);
X
X	if (header_table[current-1].subject[0] != '\0') {
X	  if ((strncmp("Re:", header_table[current-1].subject, 3) == 0) ||
X	      (strncmp("RE:", header_table[current-1].subject, 3) == 0) ||
X	      (strncmp("re:", header_table[current-1].subject, 3) == 0)) 
X	    strcpy(subject, header_table[current-1].subject);
X	  else {
X	    strcpy(subject,"Re: ");
X	    strcat(subject,header_table[current-1].subject); 
X	  }
X	  return_value = sendmsg(return_address, full_address, subject, 
X				 TRUE, NO, TRUE);
X	}
X	else
X	  return_value = sendmsg(return_address, full_address, 
X			      "Re: your mail", TRUE, NO, TRUE);
X
X	return(return_value);
X
X}
X
Xint
Xforward()
X{
X	/** Forward the current message.  What this actually does is
X	    to set auto_copy to true, then call 'send' to get the 
X	    address and route the mail.   Modified to also set
X	    'noheader' to FALSE also, so that the original headers
X	    of the message sent are included in the message body also.
X	    Return TRUE if the main part of the screen has been changed
X	    (useful for knowing whether a redraw is needed.
X	**/
X
X	char subject[SLEN], address[VERY_LONG_STRING];
X	int  original_cc, results, edit_msg = FALSE;
X
X	forwarding = TRUE;
X
X	original_cc = auto_copy;
X	address[0] = '\0';
X
X	if (header_table[current-1].status & FORM_LETTER)
X	  PutLine0(LINES-3,COLUMNS-40,"<no editing allowed>");
X	else {
X	  edit_msg = (want_to("Edit outgoing message (y/n) ? ",'y',FALSE)!='n');
X	  Write_to_screen("%s", 1, edit_msg? "Yes" : "No");
X	}
X
X	auto_cc = TRUE;			/* we want a copy */
X
X	if (strlen(header_table[current-1].subject) > 0) {
X
X	  strcpy(subject, header_table[current-1].subject); 
X
X	  /* this next strange compare is to see if the last few chars are
X	     already '(fwd)' before we tack another on */
X
X	  if (strlen(subject) < 6 || (strcmp((char *) subject+strlen(subject)-5,
X					     "(fwd)") != 0))
X	    strcat(subject, " (fwd)");
X
X	  results = sendmsg(address, "", subject, edit_msg,
X	    header_table[current-1].status & FORM_LETTER? 
X	    PREFORMATTED : allow_forms, FALSE);
X	}
X	else
X	  results = sendmsg(address, "", "Forwarded mail...", edit_msg, 
X	    header_table[current-1].status & FORM_LETTER? 
X	    PREFORMATTED : allow_forms, FALSE);
X	
X	auto_copy = original_cc;
X	forwarding = FALSE;
X
X	return(results);
X}
X
Xget_and_expand_everyone(return_address, full_address)
Xchar *return_address, *full_address;
X{
X	/** Read the current message, extracting addresses from the 'To:'
X	    and 'Cc:' lines.   As each address is taken, ensure that it
X	    isn't to the author of the message NOR to us.  If neither,
X	    prepend with current return address and append to the 
X	    'full_address' string.
X	**/
X
X    char ret_address[LONG_SLEN], buf[LONG_SLEN], new_address[LONG_SLEN],
X	 address[LONG_SLEN], comment[LONG_SLEN];
X    int  in_message = 1, first_pass = 0, index, line_pending = 0;
X
X    /** First off, get to the first line of the message desired **/
X
X    if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
X	dprint(1,(debugfile,"Error: seek %ld resulted in errno %s (%s)\n", 
X		 header_table[current-1].offset, error_name(errno), 
X		 "get_and_expand_everyone"));
X	error2("ELM [seek] couldn't read %d bytes into file (%s)",
X	       header_table[current-1].offset, error_name(errno));
X	return;
X    }
X 
X    /** okay!  Now we're there!  **/
X
X    /** let's fix the ret_address to reflect the return address of this
X	message with '%s' instead of the persons login name... **/
X
X    translate_return(return_address, ret_address);
X
X    /** now let's parse the actual message! **/
X
X    while (in_message) {
X      if (! line_pending)
X        in_message = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL);
X      line_pending = 0;
X      if (first_word(buf, "From ") && first_pass++ != 0) 
X	in_message = FALSE;
X      else if (first_word(buf, "To:") || first_word(buf, "Cc:") ||
X	       first_word(buf, "CC:") || first_word(buf, "cc:")) {
X	do {
X	  no_ret(buf);
X
X	  /** we have a buffer with a list of addresses, each of either the
X	      form "address (name)" or "name <address>".  Our mission, should
X	      we decide not to be too lazy, is to break it into the two parts.
X	  **/
X	      
X	  if (!whitespace(buf[0]))
X	    index = chloc(buf, ':')+1;		/* skip header field */
X	  else
X	    index = 0;				/* skip whitespace   */
X
X	  while (break_down_tolist(buf, &index, address, comment)) {
X
X	    if (okay_address(address, return_address)) {
X	      sprintf(new_address, ret_address, address);
X	      optimize_and_add(new_address, full_address);
X	    }
X	  }
X
X          in_message = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL);
X
X	  if (in_message) dprint(2, (debugfile, "> %s", buf));
X	
X	} while (in_message && whitespace(buf[0]));
X	line_pending++;
X      }
X      else if (strlen(buf) < 2)	/* done with header */
X	 in_message = FALSE;
X    }
X}
X
Xint
Xokay_address(address, return_address)
Xchar *address, *return_address;
X{
X	/** This routine checks to ensure that the address we just got
X	    from the "To:" or "Cc:" line isn't us AND isn't the person	
X	    who sent the message.  Returns true iff neither is the case **/
X
X	char our_address[SLEN];
X	struct addr_rec  *alternatives;
X
X	if (in_string(address, return_address))
X	  return(FALSE);
X
X	sprintf(our_address, "%s!%s", hostname, username);
X
X	if (in_string(address, our_address))
X	  return(FALSE);
X
X	sprintf(our_address, "%s@%s", username, hostname);
X	  
X	if (in_string(address, our_address))
X	  return(FALSE);
X
X	alternatives = alternative_addresses;
X
X	while (alternatives != NULL) {
X	  if (in_string(address, alternatives->address))
X	    return(FALSE);
X	  alternatives = alternatives->next;
X	}
X
X	return(TRUE);
X}
X	    
Xoptimize_and_add(new_address, full_address)
Xchar *new_address, *full_address;
X{
X	/** This routine will add the new address to the list of addresses
X	    in the full address buffer IFF it doesn't already occur.  It
X	    will also try to fix dumb hops if possible, specifically hops
X	    of the form ...a!b...!a... and hops of the form a@b@b etc 
X	**/
X
X	register int len, host_count = 0, i;
X	char     hosts[MAX_HOPS][SLEN];	/* array of machine names */
X	char     *host, *addrptr;
X
X	if (in_string(full_address, new_address))
X	  return(1);	/* duplicate address */
X
X	/** optimize **/
X	/*  break down into a list of machine names, checking as we go along */
X	
X	addrptr = (char *) new_address;
X
X	while ((host = get_token(addrptr, "!", 1)) != NULL) {
X	  for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
X	      ;
X
X	  if (i == host_count) {
X	    strcpy(hosts[host_count++], host);
X	    if (host_count == MAX_HOPS) {
X	       dprint(2, (debugfile,
X              "Error: hit max_hops limit trying to build return address (%s)\n",
X		      "optimize_and_add"));
X	       error("Can't build return address - hit MAX_HOPS limit!");
X	       return(1);
X	    }
X	  }
X	  else 
X	    host_count = i + 1;
X	  addrptr = NULL;
X	}
X
X	/** fix the ARPA addresses, if needed **/
X	
X	if (chloc(hosts[host_count-1], '@') > -1)
X	  fix_arpa_address(hosts[host_count-1]);
X	  
X	/** rebuild the address.. **/
X
X	new_address[0] = '\0';
X
X	for (i = 0; i < host_count; i++)
X	  sprintf(new_address, "%s%s%s", new_address, 
X	          new_address[0] == '\0'? "" : "!",
X	          hosts[i]);
X
X	if (full_address[0] == '\0')
X	  strcpy(full_address, new_address);
X	else {
X	  len = strlen(full_address);
X	  full_address[len  ] = ',';
X	  full_address[len+1] = ' ';
X	  full_address[len+2] = '\0';
X	  strcat(full_address, new_address);
X	}
X
X	return(0);
X}
X
Xget_return_name(address, name, trans_to_lowercase)
Xchar *address, *name;
Xint   trans_to_lowercase;
X{
X	/** Given the address (either a single address or a combined list 
X	    of addresses) extract the login name of the first person on
X	    the list and return it as 'name'.  Modified to stop at
X	    any non-alphanumeric character. **/
X
X	/** An important note to remember is that it isn't vital that this
X	    always returns just the login name, but rather that it always
X	    returns the SAME name.  If the persons' login happens to be,
X	    for example, joe.richards, then it's arguable if the name 
X	    should be joe, or the full login.  It's really immaterial, as
X	    indicated before, so long as we ALWAYS return the same name! **/
X
X	/** Another note: modified to return the argument as all lowercase
X	    always, unless trans_to_lowercase is FALSE... **/
X
X	char single_address[LONG_SLEN];
X	register int i, loc, index = 0;
X
X	dprint(6, (debugfile,"get_return_name called with (%s, <>, shift=%s)\n",
X		   address, onoff(trans_to_lowercase)));
X
X	/* First step - copy address up to a comma, space, or EOLN */
X
X	for (i=0; address[i] != ',' && ! whitespace(address[i]) && 
X	     address[i] != '\0'; i++)
X	  single_address[i] = address[i];
X	single_address[i] = '\0';
X
X	/* Now is it an ARPA address?? */
X
X	if ((loc = chloc(single_address, '@')) != -1) {	  /* Yes */
X
X	  /* At this point the algorithm is to keep shifting our copy 
X	     window left until we hit a '!'.  The login name is then
X             located between the '!' and the first metacharacter to 
X	     it's right (ie '%', ':' or '@'). */
X
X	  for (i=loc; single_address[i] != '!' && i > -1; i--)
X	      if (single_address[i] == '%' || 
X	          single_address[i] == ':' ||
X	          single_address[i] == '.' ||	/* no domains */
X		  single_address[i] == '@') loc = i-1;
X	
X	  if (i < 0 || single_address[i] == '!') i++;
X
X	  for (index = 0; index < loc - i + 1; index++)
X	    if (trans_to_lowercase)
X	      name[index] = tolower(single_address[index+i]);
X	    else
X	      name[index] = single_address[index+i];
X	  name[index] = '\0';
X	}
X	else {	/* easier - standard USENET address */
X
X	  /* This really is easier - we just cruise left from the end of
X	     the string until we hit either a '!' or the beginning of the
X             line.  No sweat. */
X
X	  loc = strlen(single_address)-1; 	/* last char */
X
X	  for (i = loc; single_address[i] != '!' && single_address[i] != '.' 
X	       && i > -1; i--)
X	     if (trans_to_lowercase)
X	       name[index++] = tolower(single_address[i]);
X	     else
X	       name[index++] = single_address[i];
X	  name[index] = '\0';
X	  reverse(name);
X	}
X}
X
Xint
Xbreak_down_tolist(buf, index, address, comment)
Xchar *buf, *address, *comment;
Xint  *index;
X{
X	/** This routine steps through "buf" and extracts a single address
X	    entry.  This entry can be of any of the following forms;
X
X		address (name)
X		name <address>
X		address
X	
X	    Once it's extracted a single entry, it will then return it as
X	    two tokens, with 'name' (e.g. comment) surrounded by parens.
X	    Returns ZERO if done with the string...
X	**/
X
X	char buffer[LONG_STRING];
X	register int i, loc = 0, hold_index;
X
X	if (*index > strlen(buf)) return(FALSE);
X
X	while (whitespace(buf[*index])) (*index)++;
X
X	if (*index > strlen(buf)) return(FALSE);
X
X	/** Now we're pointing at the first character of the token! **/
X
X	hold_index = *index;
X
X	while (buf[*index] != ',' && buf[*index] != '\0')
X	  buffer[loc++] = buf[(*index)++];
X
X	(*index)++;
X	buffer[loc] = '\0';
X
X	while (whitespace(buffer[loc])) 	/* remove trailing whitespace */
X	  buffer[--loc] = '\0';
X
X	if (strlen(buffer) == 0) return(FALSE);
X
X	dprint(5, (debugfile, "\n* got \"%s\"\n", buffer));
X
X	if (buffer[loc-1] == ')') {	/*   address (name)  format */
X	  for (loc = 0;buffer[loc] != '(' && loc < strlen(buffer); loc++)
X		/* get to the opening comment character... */ ;
X
X	  loc--;	/* back up to just before the paren */
X	  while (whitespace(buffer[loc])) loc--;	/* back up */
X
X	  /** get the address field... **/
X
X	  for (i=0; i <= loc; i++)
X	    address[i] = buffer[i];
X	  address[i] = '\0';
X
X	  /** now get the comment field, en toto! **/
X
X	  loc = 0;
X
X	  for (i = chloc(buffer, '('); i < strlen(buffer); i++)
X	    comment[loc++] = buffer[i];
X	  comment[loc] = '\0';
X	}
X	else if (buffer[loc-1] == '>') {	/*   name <address>  format */
X	  dprint(7, (debugfile, "\tcomment <address>\n"));
X	  for (loc = 0;buffer[loc] != '<' && loc < strlen(buffer); loc++)
X		/* get to the opening comment character... */ ;
X	  while (whitespace(buffer[loc])) loc--;	/* back up */
X
X	  /** get the comment field... **/
X
X	  comment[0] = '(';
X	  for (i=1; i < loc; i++)
X	    comment[i] = buffer[i-1];
X	  comment[i++] = ')';
X	  comment[i] = '\0';
X
X	  /** now get the address field, en toto! **/
X
X	  loc = 0;
X
X	  for (i = chloc(buffer,'<') + 1; i < strlen(buffer) - 1; i++)
X	    address[loc++] = buffer[i];
X	
X	  address[loc] = '\0';
X	}
X	else {
X	  /** the next section is added so that all To: lines have commas
X	      in them accordingly **/
X
X	  for (i=0; buffer[i] != '\0'; i++)
X	    if (whitespace(buffer[i])) break;
X	  if (i < strlen(buffer)) {	/* shouldn't be whitespace */
X	    buffer[i] = '\0';
X	    *index = hold_index + strlen(buffer) + 1;
X	  }
X	  strcpy(address, buffer);
X	  comment[0] = '\0';
X	}
X
X	dprint(5, (debugfile, "-- returning '%s' '%s'\n", address, comment));
X
X	return(TRUE);
X}
SHAR_EOF
echo "File src/reply.c is complete"
chmod 0444 src/reply.c || echo "restore of src/reply.c fails"
echo "x - extracting src/returnadd.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/returnadd.c &&
X
Xstatic char rcsid[] = "@(#)$Id: returnadd.c,v 2.1 88/09/15 20:29:32 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	returnadd.c,v $
X * Revision 2.1  88/09/15  20:29:32  syd
X * checked in with -k by syd at 88.09.15.20.29.32.
X * 
X * 88/08/27 ssw
X * changes due to alpha testing at dsinc
X *	add strip off of our site name on reply
X *	so that local sigs appear on local replies - elm was putting
X *	fill sig on replies to local messages
X *
X * Revision 2.1  88/07/21  09:59:24  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:29:18  edc
X * Original 2.0 gamma sources as leaked from HP
X * 
X ******************************************************************************/
X
X/** This set of routines is used to generate real return addresses
X    and also return addresses suitable for inclusion in a users
X    alias files (ie optimized based on the pathalias database).
X
X    Added: the ability to respond to messages that were originally
X    sent by the user (That is, the "savemail" file format messages)
X    by reading the return address, seeing the "To:" prefix and then
X    calling the "get_existing_return()" routine.  Currently this does
X    NOT include any "Cc" lines in the message, just the "To:" line(s).
X
X    Also added the PREFER_UUCP stuff for listing reasonable addresses
X    and such...*sigh*
X
X**/
X
X#include "headers.h"
X
X#include <errno.h>
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
Xchar *shift_lower();
X
Xextern int errno;
X
Xchar *error_name(), *strcat(), *strcpy();
X
X#ifdef OPTIMIZE_RETURN
X
Xoptimize_return(address)
Xchar *address;
X{
X	/** This routine tries to create an optimized address, that is,
X	    an address that has the minimal information needed to 
X	    route a message to this person given the current path
X	    database...
X	**/
X
X#ifdef PREFER_UUCP
X
X	/** first off, let's see if we need to strip off the localhost
X	    address crap... **/
X
X	/** if we have a uucp part (e.g.a!b) AND the bogus address...**/
X
X	if (chloc(address,'!') != -1 && in_string(address, BOGUS_INTERNET))
X	  address[strlen(address)-strlen(BOGUS_INTERNET)] = '\0';
X#endif
X
X	/** next step is to figure out what sort of address we have... **/
X
X	if (chloc(address, '%') != -1)
X	  optimize_cmplx_arpa(address);
X	else if (chloc(address, '@') != -1)
X	  optimize_arpa(address);
X	else
X	  optimize_usenet(address);
X}
X
Xoptimize_cmplx_arpa(address)
Xchar *address;
X{
X	/** Try to optimize a complex ARPA address.  A Complex address is one 
X	    that contains '%' (deferred '@').  For example:  
X		veeger!hpcnof!hplabs!joe%sytech@syte  
X	    is a complex address (no kidding, right?).  The algorithm for 
X	    trying to resolve it is to move all the way to the right, then 
X	    back up left until the first '!' then from there to the SECOND 
X	    metacharacter on the right is the name@host address...(in this 
X            example, it would be "joe%sytech").  Check this in the routing
X	    table.  If not present, keep backing out to the right until we
X	    find a host that is present, or we hit the '@' sign.  Once we
X	    have a 'normal' ARPA address, hand it to optimize_arpa().
X	**/
X
X	char name[SHORT_SLEN], buffer[SLEN], junk[SLEN];
X	char host[SHORT_SLEN], old_host[SHORT_SLEN];
X	register int i, loc, nloc = 0, hloc = 0, passes = 1;
X
X	/** first off, get the name%host... **/
X
X	for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--)
X	   ;
X
X	while (address[loc] != '\0') {
X
X	  if (passes == 1) {
X	    loc++;
X
X	    while (address[loc] != '%' && address[loc] != '@')
X	      name[nloc++] = address[loc++];
X	  }
X	  else {
X	    for (i=0; old_host[i] != '\0'; i++)
X	      name[nloc++] = old_host[i];
X	  }
X
X	  loc++;
X  
X	  while (address[loc] != '%' && address[loc] != '@')
X	    host[hloc++] = address[loc++];
X  
X	  host[hloc] = name[nloc] = '\0';
X
X	  strcpy(old_host, host);
X
X	  sprintf(buffer, "%s@%s", name, shift_lower(host));
X
X	  if (expand_site(buffer, junk) == 0) {
X	    strcpy(address, buffer);
X	    return;
X	  }
X	  else if (address[loc] == '@') {
X	    optimize_arpa(address);
X	    return;
X	  }
X	  else
X	    name[nloc++] = '%';	/* for next pass through */
X
X	}
X}
X
Xoptimize_arpa(address)
Xchar *address;
X{
X	/** Get an arpa address and simplify it to the minimal
X	    route needed to get mail to this person... **/
X
X	char name[SHORT_SLEN], buffer[SLEN], junk[SLEN];
X	char host[SHORT_SLEN];
X	register int loc, nloc = 0, hloc = 0, at_sign = 0;
X
X	for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) {
X	  if (address[loc] == '@')
X	     at_sign++;	/* remember this spot! */
X	  else if (at_sign)
X	    name[nloc++] = address[loc];
X	  else
X	    host[hloc++] = address[loc];
X	}
X
X	name[nloc] = host[hloc] = '\0';
X
X	reverse(name);
X	reverse(host);
X
X	sprintf(buffer,"%s@%s", name, shift_lower(host));
X
X	if (expand_site(buffer, junk) == 0) {
X	  strcpy(address, buffer);
X	  return;
X	}
X
X	optimize_usenet(address);	/* that didn't work... */
X}
X	
Xoptimize_usenet(address)
Xchar *address;
X{
X	/** optimize the return address IFF it's a standard usenet
X	    address...
X	**/
X
X	char name[SHORT_SLEN],  new_address[SLEN], buffer[SLEN], junk[SLEN];
X	register int loc, nloc = 0, aloc = 0, passes = 1;
X
X	for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) 
X	  name[nloc++] = address[loc];
X	name[nloc] = '\0';
X
X	reverse(name);
X
X	new_address[0] = '\0';	
X
X	/* got name, now get machine until we can get outta here */
X
X	while (loc > -1) {
X
X	  new_address[aloc++] = address[loc--];	/* the '!' char */
X
X	  while (address[loc] != '!' && loc > -1)
X	    new_address[aloc++] = address[loc--];
X
X	  new_address[aloc] = '\0';
X
X	  strcpy(buffer, new_address);
X	  reverse(buffer);
X	
X	  if (expand_site(buffer, junk) == 0) {
X	    if (passes == 1 && chloc(name, '@') == -1) {
X	      buffer[strlen(buffer) - 1] = '\0';	/* remove '!' */
X	      sprintf(address, "%s@%s", name, buffer);
X	    }
X	    else 
X	      sprintf(address, "%s%s", buffer, name);
X	    return;		/* success! */
X	  }
X	  passes++;
X	}
X
X	return;		/* nothing to do! */
X}
X
X#endif	OPTIMIZE_RETURN
X
Xget_return(buffer)
Xchar *buffer;
X{
X	/** reads 'current' message again, building up the full return 
X	    address including all machines that might have forwarded 
X	    the message.  **/
X
X	char buf[LONG_SLEN], name1[SLEN], name2[SLEN], lastname[SLEN];
X	char hold_return[LONG_SLEN], alt_name2[SLEN], *cptr;
X	int ok = 1, lines;
X
X	/* now initialize all the char buffers [thanks Keith!] */
X
X	buf[0] = name1[0] = name2[0] = lastname[0] = '\0';
X	hold_return[0] = alt_name2[0] = '\0';
X
X	/** get to the first line of the message desired **/
X
X	if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
X	  dprint(1, (debugfile,
X		"Error: seek %ld bytes into file hit errno %s (%s)", 
X		header_table[current-1].offset, error_name(errno), 
X	        "get_return"));
X	  error2("couldn't seek %d bytes into file (%s)",
X	       header_table[current-1].offset, error_name(errno));
X	  return;
X	}
X 
X	/** okay!  Now we're there!  **/
X
X	lines = header_table[current-1].lines;
X
X	buffer[0] = '\0';
X
X	while (ok && lines--) {
X	  ok = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL);
X	  if (first_word(buf, "From ")) 
X	    sscanf(buf, "%*s %s", hold_return);
X	  else if (first_word(buf, ">From")) {
X	    sscanf(buf,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s %s", 
X	           name1, name2, alt_name2);
X	    if (strcmp(name2, "from") == 0)		/* remote from xyz  */
X	      strcpy(name2, alt_name2);
X	    else if (strcmp(name2, "by") == 0)	/* forwarded by xyz */
X	      strcpy(name2, alt_name2);
X	    add_site(buffer, name2, lastname);
X	  }
X
X#ifdef USE_EMBEDDED_ADDRESSES
X
X	  else if (first_word(buf, "From:")) {
X	    get_address_from("From:", buf, hold_return);
X	    buffer[0] = '\0';
X          }
X          else if (first_word(buf, "Reply-To:")) {
X	    get_address_from("Reply-To:", buf, buffer);
X	    return;
X          }
X
X#endif
X
X	  else if (strlen(buf) < 2)	/* done with header */
X            lines = 0; /* let's get outta here!  We're done!!! */
X	}
X
X	if (buffer[0] == '\0')
X	  strcpy(buffer, hold_return); /* default address! */
X	else
X	  add_site(buffer, name1, lastname);	/* get the user name too! */
X
X	if ((ok = chloc(buffer, '!')) >= 0)
X		{
X		cptr = BOGUS_INTERNET;
X		strcpy(buf, cptr + 1);
X		if ((lines = chloc(buf, '.')) >= 0)
X			buf[lines] = '\0';
X		strcat(buf, "!");
X		if (strncmp(buf, buffer, strlen(buf)) == 0) /* strip off our node name */
X			{
X			strcpy(buf, buffer);
X			strcpy(buffer, &buf[ok + 1]);
X			}
X		}
X
X	if ((ok = chloc(buffer, '@')) >= 0)
X		{
X		strcpy(buf, BOGUS_INTERNET);
X		if (strcmp(&buffer[ok], buf) == 0)
X			buffer[ok] = '\0';	/* strip off our node name on reply */
X		else
X			{
X			if ((lines = chloc(buf, '.')) >= 0)
X				buf[lines] = '\0';
X			if (strcmp(&buffer[ok], buf) == 0)
X				buffer[ok] = '\0';	/* strip off our node name on reply */
X			}
X		}
X
X	if (first_word(buffer, "To:"))	/* response to savecopy!  */
X	  get_existing_address(buffer);
X        else 
X          /* if we have a space character, or we DON'T have '!' or '@' chars */
X
X          if (chloc(header_table[current-1].from, ' ') >= 0 ||
X	     (chloc(header_table[current-1].from, '!') < 0 &&
X	      chloc(header_table[current-1].from, '@') < 0)) {
X	       sprintf(name2, " (%s)", header_table[current-1].from);
X	       strcat(buffer, name2);
X          }
X}
X
Xget_existing_address(buffer)
Xchar *buffer;
X{
X	/** This routine is called when the message being responded to has
X	    "To:xyz" as the return address, signifying that this message is
X	    an automatically saved copy of a message previously sent.  The
X	    correct to address can be obtained fairly simply by reading the
X	    To: header from the message itself and (blindly) copying it to
X	    the given buffer.  Note that this header can be either a normal
X	    "To:" line (Elm) or "Originally-To:" (previous versions e.g.Msg)
X	**/
X
X	char mybuf[LONG_STRING];
X	register char ok = 1, in_to = 0;
X
X	buffer[0] = '\0';
X
X	/** first off, let's get to the beginning of the message... **/
X
X        if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
X	    dprint(1, (debugfile, 
X		    "Error: seek %ld bytes into file hit errno %s (%s)", 
X		    header_table[current-1].offset, error_name(errno), 
X		    "get_existing_address"));
X	    error2("couldn't seek %d bytes into the file (%s)",
X	           header_table[current-1].offset, error_name(errno));
X	    return;
X        }
X 
X        /** okay!  Now we're there!  **/
X
X        while (ok) {
X          ok = (int) (fgets(mybuf, LONG_STRING, mailfile) != NULL);
X	  no_ret(mybuf);	/* remove return character */
X
X          if (first_word(mybuf, "To: ")) {
X	    in_to = TRUE;
X	    strcpy(buffer, (char *) mybuf + strlen("To: "));
X          }
X	  else if (first_word(mybuf, "Original-To:")) {
X	    in_to = TRUE;
X	    strcpy(buffer, (char *) mybuf + strlen("Original-To:"));
X	  }
X	  else if (in_to && whitespace(mybuf[0])) {
X	    strcat(buffer, " ");		/* tag a space in   */
X	    strcat(buffer, (char *) mybuf + 1);	/* skip 1 whitespace */
X	  }
X	  else if (strlen(mybuf) < 2)
X	    return;				/* we're done for!  */
X	  else
X	    in_to = 0;
X      }
X}
SHAR_EOF
chmod 0444 src/returnadd.c || echo "restore of src/returnadd.c fails"
echo "x - extracting src/save_opts.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/save_opts.c &&
X
Xstatic char rcsid[] = "@(#)$Id: save_opts.c,v 2.1 88/09/15 20:29:35 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987, 1988 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	save_opts.c,v $
X * Revision 2.1  88/09/15  20:29:35  syd
X * checked in with -k by syd at 88.09.15.20.29.35.
X * 
X * 88/09/13  Chip Rosenthal <chip@vector>
X * fix crash with segmentation violation if you try to save the
X * configuration while in the options menu and the "elmrc-info"
X * file is not found.
X *
X * 88/09/02 Syd Weinstein
X *	added version buffer
X *
X * 88/09/01 Rob Bernardo <rob@pbhyf.PacBell.COM>
X *	fixes not saving ask_cc option in elmrc
X *
X * 88/08/27 ssw
X *	add deluth patches
X *
X * Revision 2.1  88/07/21  09:59:28  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:32  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** This file contains the routine needed to allow the users to change the
X    Elm parameters and then save the configuration in a ".elm/elmrc" file in
X    their home directory.  With any luck this will allow them never to have
X    to actually EDIT the file!!
X
X**/
X
X#include "headers.h"
X#include <errno.h>
X
X#undef onoff
X#define   onoff(n)	(n == 1? "ON":"OFF")
X
X#define absolute(x)		((x) < 0? -(x) : (x))
X
Xextern  int errno;
Xextern char version_buff[];
X
Xchar *error_name(), *sort_name();
Xlong  ftell();
X
X#include "save_opts.h"
X
XFILE *elminfo;		/* informational file as needed... */
X
Xsave_options()
X{
X	/** Save the options currently specified to a file.  This is a
X	    fairly complex routine since it tries to put in meaningful
X	    comments and such as it goes along.  The comments are
X	    extracted from the file ELMRC_INFO as defined in the sysdefs.h
X	    file.  THAT file has the format;
X
X		varname
X		  <comment>
X		  <comment>
X		<blank line>
X
X	    and each comment is written ABOVE the variable to be added.  This
X	    program also tries to make 'pretty' stuff like the alternatives
X	    and such.
X	**/
X
X	FILE *newelmrc; 
X	char  oldfname[SLEN], newfname[SLEN];
X
X	sprintf(newfname, "%s/%s", home, elmrcfile);
X	sprintf(oldfname, "%s/%s", home, old_elmrcfile);
X
X	/** first off, let's see if they already HAVE a .elm/elmrc file **/
X
X	if (access(newfname, ACCESS_EXISTS) != -1) {
X	  /** YES!  Copy it to the file ".old.elmrc".. **/
X	  if (unlink(oldfname) < 0) 
X	    dprint(1, (debugfile, "Unable to unlink %s\n", oldfname));
X	  if (link(newfname, oldfname) < 0)
X	    dprint(2, (debugfile, "Unable to link %s to %s\n", 
X		   newfname, oldfname));
X          if (unlink(newfname) < 0)
X	    dprint(1, (debugfile, "Unable to unlink %s\n", newfname));
X	  (void) chown(oldfname, userid, groupid);
X	}
X
X	/** now let's open the datafile if we can... **/
X
X	if ((elminfo = fopen(ELMRC_INFO, "r")) == NULL) 
X	  error1("Warning: saving without comments - can't get to %s", 
X		  ELMRC_INFO);
X
X	/** next, open the new .elm/elmrc file... **/
X
X	if ((newelmrc = fopen(newfname, "w")) == NULL) {
X	   error2("Can't save configuration: can't write to %s [%s]",
X		   newfname, error_name(errno));
X	   return;
X	}
X	
X	chown(newfname, userid, groupid);
X
X	save_user_options(elminfo, newelmrc);
X
X	error1("Options saved in file %s", newfname);
X}
X
Xsave_user_options(elminfo_fd, newelmrc)
XFILE *elminfo_fd, *newelmrc;
X{
X	/** save the information in the file.  If elminfo_fd == NULL don't look
X	    for comments!
X	**/
X
X	if (elminfo_fd != NULL) 
X	  build_offset_table(elminfo_fd);
X
X	fprintf(newelmrc, 	
X	      "#\n# .elm/elmrc - options file for the Elm mail system\n#\n");
X
X	if (strlen(full_username) > 0)
X	  fprintf(newelmrc, "# Saved automatically by Elm %s for %s\n#\n\n",
X		  version_buff, full_username);
X	else
X	  fprintf(newelmrc, "# Saved automatically by Elm %s\n#\n\n", version_buff);
X
X	save_option_string(CALENDAR, calendar_file, newelmrc, FALSE);
X	save_option_string(EDITOR, editor, newelmrc, FALSE);
X	save_option_string(FULLNAME, full_username, newelmrc, FALSE);
X	save_option_string(MAILBOX, mailbox, newelmrc, FALSE);
X	save_option_string(MAILDIR, folders, newelmrc, FALSE);
X	save_option_string(PAGER, pager, newelmrc, FALSE);
X	save_option_string(PREFIX, prefixchars, newelmrc, TRUE);
X	save_option_string(PRINT, printout, newelmrc, FALSE);
X	save_option_string(SAVEMAIL, savefile, newelmrc, FALSE);
X	save_option_string(SHELL, shell, newelmrc, FALSE);
X
X	save_option_string(LOCALSIGNATURE, local_signature, newelmrc, FALSE);
X	save_option_string(REMOTESIGNATURE, remote_signature, newelmrc, FALSE);
X
X	save_option_sort(SORTBY, newelmrc);
X
X	save_option_on_off(ALWAYSDELETE, always_del, newelmrc);
X	save_option_on_off(ALWAYSLEAVE, always_leave, newelmrc);
X	save_option_on_off(ARROW, arrow_cursor, newelmrc);
X	save_option_on_off(ASK, question_me, newelmrc);
X	save_option_on_off(ASKCC, prompt_for_cc, newelmrc);
X	save_option_on_off(AUTOCOPY, auto_copy, newelmrc);
X
X	save_option_number(BOUNCEBACK, bounceback, newelmrc);
X
X	save_option_on_off(COPY, auto_cc, newelmrc);
X	save_option_on_off(FORMS, (allow_forms != NO), newelmrc);
X	save_option_on_off(KEYPAD, hp_terminal, newelmrc);
X	save_option_on_off(MENU, mini_menu, newelmrc);
X	save_option_on_off(MOVEPAGE, move_when_paged, newelmrc);
X	save_option_on_off(NAMES, names_only, newelmrc);
X	save_option_on_off(NOHEADER, noheader, newelmrc);
X	save_option_on_off(POINTNEW, point_to_new, newelmrc);
X	save_option_on_off(RESOLVE, resolve_mode, newelmrc);
X	save_option_on_off(SAVENAME, save_by_name, newelmrc);
X	save_option_on_off(SOFTKEYS, hp_softkeys, newelmrc);
X
X	save_option_number(TIMEOUT, (int) timeout, newelmrc);
X
X	save_option_on_off(TITLES, title_messages, newelmrc);
X
X	save_option_number(USERLEVEL, user_level, newelmrc);
X
X	save_option_on_off(WARNINGS, warnings, newelmrc);
X	save_option_on_off(WEED, filter, newelmrc);
X
X	save_option_weedlist(WEEDOUT, newelmrc);
X	save_option_alternatives(ALTERNATIVES, alternative_addresses, newelmrc);
X
X	if ( elminfo_fd != NULL ) {
X	    fflush(elminfo_fd);	/* make sure we're clear... */
X	    fclose(elminfo_fd);
X	}
X}
X
Xsave_option_string(index, value, fd, underscores)
Xint index, underscores;
Xchar *value;
XFILE *fd;
X{
X	/** Save a string option to the file... only subtlety is when we
X	    save strings with spaces in 'em - translate to underscores!
X	**/
X
X	register int i;
X	char     buffer[SLEN];
X	
X	if (strlen(value) == 0) return;		/* why bother? */
X
X	add_comment(index, fd);
X	
X	strcpy(buffer, value);
X
X	if (underscores)
X	  for (i=0; i < strlen(buffer); i++)
X	    if (buffer[i] == SPACE) buffer[i] = '_';
X
X	fprintf(fd, "%s = %s\n\n", save_info[index].name, buffer);
X}
X	   
Xsave_option_sort(index, fd)
Xint index;
XFILE *fd;
X{
X	/** save the current sorting option to a file **/
X
X	add_comment(index, fd);
X
X	fprintf(fd, "%s = %s\n\n", save_info[index].name,
X		sort_name(SHORT));
X}
X
Xsave_option_number(index, value, fd)
Xint index, value;
XFILE *fd;
X{
X	/** Save a binary option to the file - boy is THIS easy!! **/
X
X	add_comment(index, fd);
X	
X	fprintf(fd, "%s = %d\n\n", save_info[index].name, value);
X}
X
Xsave_option_on_off(index, value, fd)
Xint index, value;
XFILE *fd;
X{
X	/** Save a binary option to the file - boy is THIS easy!! **/
X
X	add_comment(index, fd);
X	
X	fprintf(fd, "%s = %s\n\n", save_info[index].name, onoff(value));
X}
X
Xsave_option_weedlist(index, fd)
Xint index;
XFILE *fd;
X{
X	/** save a list of weedout headers to the file **/
X
X	int length_so_far = 0, i;
X
X	add_comment(index, fd);
X
X	length_so_far = strlen(save_info[index].name) + 4;
X
X	fprintf(fd, "%s = ", save_info[index].name);
X
X	/** first off, skip till we get past the default list **/
X
X	for (i = 0; i < weedcount; i++) 
X	  if (strcmp(weedlist[i],"*end-of-defaults*") == 0)
X	    break;
X
X	while (i < weedcount) {
X	  if (strcmp(weedlist[i], "*end-of-defaults*") != 0)
X	    break;
X	  i++;	/* and get PAST it too! */
X	}
X
X	while (i < weedcount) {
X	  if (strlen(weedlist[i]) + length_so_far > 78) {
X	    fprintf(fd, "\n\t");
X	    length_so_far = 8;
X	  }
X	  fprintf(fd, "\"%s\" ", weedlist[i]);
X	  length_so_far += (strlen(weedlist[i]) + 4);
X	  i++;
X	}
X	fprintf(fd, "\t\"*end-of-user-headers*\"\n\n");
X}
X
Xsave_option_alternatives(index, list, fd)
Xint index;
Xstruct addr_rec *list;
XFILE *fd;
X{
X	/** save a list of options to the file **/
X	int length_so_far = 0;
X	struct addr_rec     *alternate;
X
X	if (list == NULL) return;	/* nothing to do! */
X
X	add_comment(index, fd);
X
X	alternate = list;	/* don't LOSE the top!! */
X
X	length_so_far = strlen(save_info[index].name) + 4;
X
X	fprintf(fd, "%s = ", save_info[index].name);
X
X	while (alternate != NULL) {
X	  if (strlen(alternate->address) + length_so_far > 78) {
X	    fprintf(fd, "\n\t");
X	    length_so_far = 8;
X	  }
X	  fprintf(fd, "%s  ", alternate->address);
X	  length_so_far += (strlen(alternate->address) + 3);
X	  alternate = alternate->next;
X	}
X	fprintf(fd, "\n\n");
X}
X
Xadd_comment(index, fd)
Xint index;
XFILE *fd;
X{	
X	/** get to and add the comment to the file **/
X	char buffer[SLEN];
X
X	/** first off, add the comment from the comment file, if available **/
X
X	if (save_info[index].offset > 0L) {
X	  if (fseek(elminfo, save_info[index].offset, 0) == -1) {
X	    dprint(1,(debugfile,
X		   "** error %s seeking to %ld in elm-info file!\n",
X		   error_name(errno), save_info[index].offset));
X	  }
X	  else while (fgets(buffer, SLEN, elminfo) != NULL) {
X	    if (buffer[0] != '#') 
X	       break;
X	    else
X	       fprintf(fd, "%s", buffer);
X	  }
X	}
X}
X
Xbuild_offset_table(elminfo_fd)
XFILE *elminfo_fd;
X{
X	/** read in the info file and build the table of offsets.
X	    This is a rather laborious puppy, but at least we can
X	    do a binary search through the array for each element and
X	    then we have it all at once!
X	**/
X
X	char line_buffer[SLEN];
X	
X	while (fgets(line_buffer, SLEN, elminfo_fd) != NULL) {
X	  if (strlen(line_buffer) > 1)
X	    if (line_buffer[0] != '#' && !whitespace(line_buffer[0])) {
X	       no_ret(line_buffer);
X	       if (find_and_store_loc(line_buffer, ftell(elminfo_fd))) {
X	         dprint(1, (debugfile,"** Couldn't find and store \"%s\" **\n", 
X			 line_buffer));
X	       }
X	    }
X	}
X}
X
Xfind_and_store_loc(name, offset)
Xchar *name;
Xlong  offset;
X{
X	/** given the name and offset, find it in the table and store it **/
X
X	int first = 0, last, middle, compare;
X
X	last = NUMBER_OF_SAVEABLE_OPTIONS;
X
X	while (first <= last) {
X
X	  middle = (first+last) / 2;
X
X	  if ((compare = strcmp(name, save_info[middle].name)) < 0) /* a < b */
X	    last = middle - 1;
X	  else if (compare == 0) {				    /* a = b */
X	    save_info[middle].offset = offset;
X	    return(0);
X	  }
X	  else  /* greater */					    /* a > b */
X	    first = middle + 1; 
X	}
X
X	return(-1);
X}
SHAR_EOF
chmod 0444 src/save_opts.c || echo "restore of src/save_opts.c fails"
echo "x - extracting src/savecopy.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/savecopy.c &&
X
Xstatic char rcsid[] = "@(#)$Id: savecopy.c,v 2.1 88/09/16 10:17:08 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	savecopy.c,v $
X * Revision 2.1  88/09/16  10:17:08  syd
X * From Rob:
X * when saving to named files, the send version didn't lowercase the name,
X * the received one did.  This fix makes the send use the lowercase name
X * 
X * Revision 2.0  88/07/21  09:59:30  edc
X * checked in with -k by syd at 88.09.15.20.29.37.
X * 
X * Revision 2.1  88/07/21  09:59:30  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:33  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** Save a copy of the specified message in the users savemail mailbox.
X
X**/
X
X#include "headers.h"
X#ifdef BSD
X# ifdef BSD4_1
X#  include <time.h>
X# else
X#  include <sys/time.h>
X# endif
X#else
X#  include <time.h>
X#endif
X
X#include <errno.h>
X
X#define  metachar(c)	(c == '+' || c == '%' || c == '=')
X
Xchar *format_long(), *get_arpa_date();
Xchar *error_name(), *error_description();
Xchar *ctime();
X
Xextern char in_reply_to[SLEN];	/* In-Reply-To: string */
Xextern int gotten_key;		/* for encryption      */
Xextern int errno;
X
Xchar *strcat(), *strcpy();
Xunsigned long sleep();
Xlong  time();
X
Xsave_copy(subject, to, cc, filename, original_to)
Xchar *subject, *to, *cc, *filename, *original_to;
X{
X	/** This routine appends a copy of the outgoing message to the
X	    file specified by the SAVEFILE environment variable.  **/
X
X	FILE *save,		/* file id for file to save to */
X	     *message;		/* the actual message body     */
X	long  thetime,		/* variable holder for time    */
X	      time();
X	char  buffer[SLEN],	/* read buffer 		       */
X	      savename[SLEN],	/* name of file saving into    */
X	      newbuffer[SLEN];  /* first name in 'to' line     */
X	register int i;		/* for chopping 'to' line up   */
X	int   crypted=0;	/* are we encrypting?          */
X
X	savename[0] = '\0';
X
X	if (save_by_name) {
X	  get_return_name(to, buffer, TRUE);
X	  if (strlen(buffer) == 0) {
X	    dprint(3, (debugfile,
X		   "Warning: get_return_name couldn't break down %s\n", to));
X	    savename[0] = '\0';
X	  }
X	  else {
X	    sprintf(savename, "%s%s%s", folders, 
X	            lastch(folders) == '/'? "" : "/", buffer);
X
X	    if (can_access(savename, READ_ACCESS) != 0)
X	      savename[0] = '\0';
X	  }
X	}
X
X	if (strlen(savename) == 0) {
X	  if (strlen(savefile) == 0) {
X	    error("variable 'SAVEFILE' not defined!");
X	    return(FALSE);
X	  }
X	  if (metachar(savefile[0])) {
X	    sprintf(savename, "%s%s%s", folders,
X		lastch(folders) == '/' ? "" : "/", savefile+1);
X	  }
X	  else
X	    strcpy(savename, savefile);
X	}
X
X	if ((errno = can_open(savename, "a"))) {
X	  dprint(2, (debugfile,
X"Error: attempt to autosave to a file that can't be appended to!\n"));
X	  dprint(2, (debugfile, "\tfilename = \"%s\"\n", savename));
X	  dprint(2, (debugfile, "** %s - %s **\n", error_name(errno),
X		  error_description(errno)));
X	  error1("permission to append to %s denied!", savename);
X	  sleep(2);
X	  return(FALSE);
X	}
X
X	if ((save = fopen(savename, "a")) == NULL) {
X	  dprint(1, (debugfile,
X		"Error: Couldn't append message to file %s (%s)\n",
X		savename, "save_copy"));
X	  dprint(1, (debugfile,"** %s - %s **\n", error_name(errno),
X		  error_description(errno)));
X	  error1("couldn't append to %s", savename);
X	  sleep(2);
X	  return(FALSE);
X	}
X
X	if ((message = fopen(filename, "r")) == NULL) {
X	  fclose(save);
X	  dprint(1, (debugfile,
X		 "Error: Couldn't read file %s (save_copy)\n", filename));
X	  dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
X		  error_description(errno)));
X	  error1("couldn't read file %s!", filename);
X	  sleep(2);
X	  return(FALSE);
X	}
X
X	for (i=0; original_to[i] != '\0' && ! whitespace(original_to[i]); i++)
X	  newbuffer[i] = original_to[i];
X
X	newbuffer[i] = '\0';
X
X	tail_of(newbuffer, buffer, FALSE);
X
X	thetime = time((long *) 0);      /* dumb dumb dumb routine */
X
X	fprintf(save,"From To:%s %s", buffer, ctime(&thetime));
X
X	fprintf(save, "Date: %s\n", get_arpa_date());
X			
X	fprintf(save,"To: %s\nSubject: %s\n", 
X		format_long(to,strlen("To: ")), subject);
X
X	if (strlen(cc) > 0)
X	  fprintf(save,"Cc: %s\n", 
X		  format_long(cc, strlen("Cc:")));
X
X	if (strlen(in_reply_to) > 0)
X	  fprintf(save, "In-Reply-To: %s\n", in_reply_to);
X
X	(void) putc('\n', save);	/* put another return, please! */
X
X	/** now copy over the message... **/
X
X	while (fgets(buffer, SLEN, message) != NULL) {
X	  if (buffer[0] == '[') {
X	    if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0)
X	      crypted = 1;
X	    else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0)
X	      crypted = 0;
X	    else if (strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0 ||
X	             strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2)) == 0) {
X
X		/* second test added due to an imcompatability between the
X		   documentation and the software!  (Thanks Bill!) */
X
X	      fclose(message);
X	      fclose(save);
X	      chown(savename, userid, groupid);	
X	      return(TRUE);
X	    }
X	  }
X	  else if (crypted) {
X	    if (! gotten_key++)
X	      getkey(ON);
X	    encode(buffer);
X	  }
X	  if (strncmp(buffer, "From ", 5) == 0)
X	    fprintf(save, ">%s", buffer);
X	  else
X	    fputs(buffer, save);
X	}
X
X	fprintf(save, "\n");	/* ensure a blank line at the end */
X
X	fclose(message);
X	fclose(save);
X
X	/* make sure save file isn't owned by root! */
X	chown(savename, userid, groupid);	
X
X	return(TRUE);
X}
SHAR_EOF
chmod 0444 src/savecopy.c || echo "restore of src/savecopy.c fails"
echo "x - extracting src/screen.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/screen.c &&
X
Xstatic char rcsid[] = "@(#)$Id: screen.c,v 2.1 88/09/16 13:51:16 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1985 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	screen.c,v $
X * Revision 2.1  88/09/16  13:51:16  syd
X * changes from Rob re Dave Taylor's comments on Magic Cookie terminals doing move
X * after attribute changes
X * 
X * Revision 2.0  88/09/15  20:29:40  syd
X * checked in with -k by syd at 88.09.15.20.29.40.
X * 
X * 88/09/02 Syd Weinstein
X *	added version buffer
X *
X * 88/09/01 Rob Bernardo <gatech!pbhyf.PacBell.COM!rob>
X *	There were *two* bugs causing the "command:" prompt to appear
X *	on the wrong line under a limited set of circumstances.
X *
X * Revision 2.1  88/07/21  09:59:32  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:34  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/**  screen display routines for ELM program 
X
X**/
X
X#include "headers.h"
X
X#define  minimum(a,b)	((a) < (b) ? (a) : (b))
X
Xstatic   int  last_current	 = -1;
X
Xchar *strcpy(), *strncpy(), *nameof(), *show_status();
X
Xextern char version_buff[];
X
Xshowscreen()
X{
X	char buffer[SLEN];
X
X	ClearScreen();
X
X	if (selected)
X	  sprintf(buffer, 
X		"Mailbox is '%s' with %d shown out of %d [Elm %s]",
X	      nameof(infile), selected, message_count, version_buff);
X	else
X	  sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]",
X	      nameof(infile), message_count, plural(message_count), version_buff);
X	Centerline(1, buffer);
X
X	last_header_page = -1;	 	/* force a redraw regardless */
X	show_headers();
X
X	if (mini_menu)
X	  show_menu();
X
X	show_last_error();
X	
X	if (hp_terminal) 
X	  define_softkeys(MAIN);
X}
X
Xupdate_title()
X{
X	/** display a new title line, probably due to new mail arriving **/
X
X	char buffer[SLEN];
X
X	if (selected)
X	  sprintf(buffer, 
X		"Mailbox is '%s' with %d shown out of %d [Elm %s]",
X	      nameof(infile), selected, message_count, version_buff);
X	else
X	  sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]",
X	      nameof(infile), message_count, plural(message_count), version_buff);
X
X	ClearLine(1);
X
X	Centerline(1, buffer);
X}
X
Xshow_menu()
X{
X	/** write main system menu... **/
X
X	if (user_level == 0) {	/* a rank beginner.  Give less options  */
X	  Centerline(LINES-7,
X  "You can use any of the following commands by pressing the first character;");
X          Centerline(LINES-6,
X"D)elete or U)ndelete mail,  M)ail a message,  R)eply or F)orward mail,  Q)uit");
X	  Centerline(LINES-5,
X  "To read a message, press <return>.  j = move down, k = move up, ? = help");
X	} else {
X	Centerline(LINES-7,
X  "|=pipe, !=shell, ?=help, <n>=set current to n, /=search pattern");
X        Centerline(LINES-6,
X"A)lias, C)hange mailbox, D)elete, E)dit, F)orward, G)roup reply, M)ail,"); 
X	Centerline(LINES-5,
X  "N)ext, O)ptions, P)rint, R)eply, S)ave, T)ag, Q)uit, U)ndelete, or eX)it");
X	}
X}
X
Xint
Xshow_headers()
X{
X	/** Display page of headers (10) if present.  First check to 
X	    ensure that header_page is in bounds, fixing silently if not.
X	    If out of bounds, return zero, else return non-zero 
X	    Modified to only show headers that are "visible" to ze human
X	    person using ze program, eh?
X	**/
X
X	register int this_msg = 0, line = 4, last = 0, last_line, 
X		     displayed = 0;
X	char newfrom[SLEN], buffer[SLEN];
X	
X	if (fix_header_page())
X	  return(FALSE);
X
X	if (selected) {
X	  if ((header_page*headers_per_page) > selected)
X	    return(FALSE); 	/* too far! too far! */
X
X	  if (header_page == 0) {
X	    this_msg = visible_to_index(1);	
X	    displayed = 0;
X	  }
X	  else {
X	    this_msg = visible_to_index(header_page * headers_per_page + 1);
X	    displayed = header_page * headers_per_page;
X	  }
X
X	  last = displayed+headers_per_page;
X
X	}
X	else {
X	  if (header_page == last_header_page) 	/* nothing to do! */
SHAR_EOF
echo "End of part 17"
echo "File src/screen.c is continued in part 18"
echo "18" > s2_seq_.tmp
exit 0
-- 
=====================================================================
Sydney S. Weinstein, CDP, CCP                   Elm Coordinator
Datacomp Systems, Inc.				Voice: (215) 947-9900
{allegra,bellcore,bpa,vu-vlsi}!dsinc!syd	FAX:   (215) 938-0235