[mod.sources] Msg Shar.part.5

sources-request@panda.UUCP (03/03/86)

Mod.sources:  Volume 4, Issue 9
Submitted by: decvax!hplabs!hpcnou!dat (Dave Taylor)

# Msg Shar part 5 of 7

# Shell Archive created by hpcnou!dat at Wed Feb 26 15:56:34 1986

# To unpack the enclosed files, please use this file as input to the
# Bourne (sh) shell.  This can be most easily done by the command;
#     sh < thisfilename

# This archive contains;
#  src/notesfile.c  src/opt_utils.c     src/output_utils.c  src/pattern.c
#  src/quit.c       src/read_rc.c       src/reply.c         src/return_addr.c
#  src/savecopy.c   src/screen.c        src/showmsg.c       src/softkeys.c


if [ ! -d src ]
then
  echo creating directory src
  mkdir src
fi

# ---------- file src/notesfile.c ----------


if [ -f src/notesfile.c ]
then
  echo File 'src/notesfile.c' already exists\!
  exit 1
fi

echo extracting file src/notesfile.c...
cat << 'END-OF-FILE' > src/notesfile.c
/**			notesfile.c			**/

/**  Routine that reads in a file comprised of saved messages from
     'notes'.  

     (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"
#include <errno.h>

char *notes_machine();

extern int errno;

int
read_notesfile()
{
	/** read in the current mailfile, assuming it's actually a set
	    of stored notes from the notes program... **/

	char buffer[LONG_STRING];
	register int line = 0, count = 0;
	long  bytes = 0L, line_bytes = 0L;
	static int first_read = 0;
	int count_x , count_y = 17;

	dprint0("read_notesfile()\n");

	if (! first_read++) {
	  MoveCursor(LINES-2, 0);
	  CleartoEOS();
	  PutLine(LINES-1, 0, "Reading in %s, message: 0", infile);
	  count_x = LINES-1;
          count_y = 22 + strlen(infile);
	}
	else {
	  PutLine(LINES-2, 0, "Reading message: 0");
	  count_x = LINES-2;
	}

	while (fgets(buffer, LONG_STRING, mailfile) != NULL) {

	  if (line == 0) {
	
	    /** Double check to make sure this is okay... if the first
	        word is "From " then we're actually reading a normal
	        file so cruise over to the read_headers() routine
		instead! **/
	
	    if (first_word(buffer, "From ")) {
	      notesfile = 0;   /* turn that bloody flag off! */
	      rewind(mailfile);  /* back up in da file... */
	      return( read_headers(FALSE) );
	    }
	  }
	
	  line_bytes = (long) strlen(buffer); 
	  line++;

	  if (first_word(buffer, "/***** ")) {
	    if (real_notes_header(buffer, &header_table[count])) {
	      header_table[count].offset = (long) bytes;
	      header_table[count].delete = 0;
	      header_table[count++].lines = line;
	      header_table[count].priority = 0;
	      PutLine(count_x, count_y, "%d", count);
	      if (count > 1)
	        header_table[count-2].lines = line -header_table[count-2].lines;
	    }
	  }
	  bytes += (long) line_bytes;
	}

	total_lines_in_file = line;
	header_table[count-1].lines = line - header_table[count-1].lines;

        rewind(mailfile);

	return(count);
}

int
real_notes_header(buffer, entry)
char *buffer;
struct header_rec *entry;
{
	/** confirms that we're looking at a REAL notes header,
	    and if so, saves it in the appropriate data entry  **/

	char subjectbuffer[SLEN], timebuffer[NLEN], lastbuffer[NLEN], 
	     am_pm[NLEN], the_day[NLEN], nullbuffer[NLEN];
	
	dprint1("real_notes_header(buffer='%s')\n", buffer);

	strcpy(nullbuffer, "NULL");
	strcpy(lastbuffer, "LAST");

	sscanf(buffer, "%*s %s %*c %s %*c %s %s %s %s %s %s", 
	    subjectbuffer, entry->from, 
	    timebuffer, am_pm, entry->month, the_day,
	    lastbuffer, nullbuffer);

	if (strcmp(lastbuffer, "LAST") == 0) 
	  return(0);
	
	if (strcmp(nullbuffer,"NULL") != 0) 
	  return(0);

	if (timebuffer[1] != ':' && timebuffer[2] != ':') 
	  return(0);

	/* now let's play format! */

	clean_up(subjectbuffer);

	sprintf(entry->subject, "Note from group %s", subjectbuffer);
	sprintf(entry->day,"%d", atoi(the_day));
	sprintf(entry->year,"%d", atoi(lastbuffer) % 100);
	sprintf(entry->time,"%s %s", timebuffer, am_pm);

	return(1);
}

char *notes_machine()
{
	/** For those rare notes posted by someone on the machine that
	    the notesfiles are saved from, this routine will fix the
	    address to ensure it's valid.  The return value will be
	    either 'machine!' or NULL if it's from the machine we're on
	**/

	static char machine_group[NLEN];
	char buffer[SLEN];
	register int i;

	dprint0("notes_machine()\n");
	
	if (fseek(mailfile, header_table[current-1].offset, 0L) != 0) {
	  error("error [notes_machine] trying to seek!");
	  dprint1("\n\tError %d trying to seek into file!\n\n", errno);
	  return( NULL );
	}

	if (fgets(buffer, SLEN, mailfile) == NULL) {
	  error("error [notes_machine] trying to read!");
	  dprint1("\n\tError %d trying to read file!\n\n", errno);
	  return( NULL );
	}

	sscanf(buffer,"%*s %s", machine_group);

	for (i=0; machine_group[i] != ':'; i++)
	   ;

	machine_group[i] = '\0';

	return( (char *) machine_group);
}
END-OF-FILE

size=`wc -c < src/notesfile.c`

if [ $size != 3955 ]
then
  echo Warning: src/notesfile.c changed - should be 3955 bytes, not $size bytes
fi

chmod 644 src/notesfile.c

# ---------- file src/opt_utils.c ----------


if [ -f src/opt_utils.c ]
then
  echo File 'src/opt_utils.c' already exists\!
  exit 1
fi

echo extracting file src/opt_utils.c...
cat << 'END-OF-FILE' > src/opt_utils.c
/**			opt_utils.c			**/

/** This file contains routines that might be needed for the various
     machines that the mailer can run on.  Please check the Makefile
     for more help and/or information. 

     (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"

#ifdef BSD
#  include <pwd.h>
#endif

#ifdef UTS
#  include <sys/utsname.h>

gethostname(hostname,size) /* get name of current host */
int size;
char *hostname;
{
	/** Return the name of the current host machine.  UTS only **/

	/** This routine compliments of Scott McGregor at the HP
	    Corporate Computing Center **/
     
	int uname();
	struct utsname name;

	(void) uname(&name);
	(void) strncpy(hostname,name.nodename,size-1);
	hostname[size] = '\0';

}

#endif /* def UTS */

#ifdef BSD

cuserid(username)
char *username;
{
	/** Added for compatibility with Bell systems, this is the last-ditch
	    attempt to get the users login name, after getlogin() fails.  It
	    instantiates "username" to the name of the user...
	**/

	struct passwd *password_entry, *getpwuid();

	password_entry = getpwuid(getuid());

	strcpy(username, password_entry->pw_name);
}

/** some supplementary string functions for Berkeley Unix systems **/

strspn(source, keys)
char *source, *keys;
{
	/** This function returns the length of the substring of
	    'source' (starting at zero) that consists ENTIRELY of
	    characters from 'keys'.  This is used to skip over a
	    defined set of characters with parsing, usually. 
	**/

	register int loc = 0, key_index = 0;

	while (source[loc] != '\0') {
	  key_index = 0;
	  while (keys[key_index] != source[loc])
	    if (keys[key_index++] == '\0')
	      return(loc);
	  loc++;
	}

	return(loc);
}

strcspn(source, keys)
char *source, *keys;
{
	/** This function returns the length of the substring of
	    'source' (starting at zero) that consists entirely of
	    characters NOT from 'keys'.  This is used to skip to a
	    defined set of characters with parsing, usually. 
	    NOTE that this is the opposite of strspn() above
	**/

	register int loc = 0, key_index = 0;

	while (source[loc] != '\0') {
	  key_index = 0;
	  while (keys[key_index] != '\0')
	    if (keys[key_index++] == source[loc])
	      return(loc);
	  loc++;
	}

	return(loc);
}

char *strtok(source, keys)
char *source, *keys;
{
	/** This function returns a pointer to the next word in source
	    with the string considered broken up at the characters 
	    contained in 'keys'.  Source should be a character pointer
	    when this routine is first called, then NULL subsequently.
	    When strtok has exhausted the source string, it will 
	    return NULL as the next word. 

	    WARNING: This routine will DESTROY the string pointed to
	    by 'source' when first invoked.  If you want to keep the
	    string, make a copy before using this routine!!
	 **/

	register int  last_ch;
	static   char *sourceptr;
		 char *return_value;

	if (source != NULL)
	  sourceptr = source;
	
	if (*sourceptr == '\0') 
	  return(NULL);		/* we hit end-of-string last time!? */

	sourceptr += strspn(sourceptr, keys);	/* skip leading crap */
	
	if (*sourceptr == '\0') 
	  return(NULL);		/* we've hit end-of-string */

	last_ch = strcspn(sourceptr, keys);	/* end of good stuff */

	return_value = sourceptr;		/* and get the ret   */

	sourceptr += last_ch;			/* ...value 	     */

	if (*sourceptr != '\0')		/* don't forget if we're at END! */
	  sourceptr++;			   /* and skipping for next time */

	return_value[last_ch] = '\0';		/* ..ending right    */
	
	return((char *) return_value);		/* and we're outta here! */
}

char *strpbrk(source, keys)
char *source, *keys;
{
	/** Returns a pointer to the first character of source that is any
	    of the specified keys, or NULL if none of the keys are present
	    in the source string. 
	**/

	register int loc = 0, key_index = 0;

	while (source[loc] != '\0') {
	  key_index = 0;
	  while (keys[key_index] != '\0')
	    if (keys[key_index++] == source[loc])
	      return((char *) (source + loc));
	  loc++;
	}
	
	return(NULL);
}

#endif
END-OF-FILE

size=`wc -c < src/opt_utils.c`

if [ $size != 4053 ]
then
  echo Warning: src/opt_utils.c changed - should be 4053 bytes, not $size bytes
fi

chmod 644 src/opt_utils.c

# ---------- file src/output_utils.c ----------


if [ -f src/output_utils.c ]
then
  echo File 'src/output_utils.c' already exists\!
  exit 1
fi

echo extracting file src/output_utils.c...
cat << 'END-OF-FILE' > src/output_utils.c
/**			output_utils.c			**/

/** This file contains routines used for output in the MSG program.

    These routines (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"


static char err_buffer[SLEN];		/* store last error message */

static char central_message_buffer[SLEN];


show_last_error()
{
	/** rewrite last error message! **/

	error(err_buffer);
}

clear_error()
{
	MoveCursor(LINES,0);
	CleartoEOLN();
	err_buffer[0] = '\0';
}

set_error(s)
char *s;
{
	strcpy(err_buffer, s);
}

error(s)
char *s;
{
	/** outputs error 's' to screen at line 22, centered! **/

	MoveCursor(LINES,0);
	CleartoEOLN();
	PutLine(LINES,(COLUMNS-strlen(s))/2,s);
	if (strlen(s) > 0)
	  dprint1("\nError: %s\n\n", s);
	fflush(stdout);
	strcpy(err_buffer, s);	/* save it too! */
}

error1(s, a)
char *s, *a;
{
	/** same as error, but with a 'printf' argument **/
	char buffer[SLEN];

	sprintf(buffer,s,a);
	error(buffer);
}

error2(s, a1, a2)
char *s, *a1, *a2;
{
	/** same as error, but with two 'printf' arguments **/
	char buffer[SLEN];

	sprintf(buffer,s, a1, a2);
	error(buffer);
}

prompt(s)
char *s;
{
	/** prompt user for input on LINES-3 line, left justified **/

	PutLine(LINES-3,0,s);
	CleartoEOLN();
}

prompt1(s,a)
char *s, *a;
{
	/** same as prompt, but with a 'printf' argument **/
	char buffer[SLEN];

	sprintf(buffer,s,a);
	prompt(buffer);
}


set_central_message(string, arg)
char *string, *arg;
{
	/** set up the given message to be displayed in the center of
	    the current window **/ 

	sprintf(central_message_buffer, string, arg);
}

display_central_message()
{
	/** display the message if set... **/

	if (central_message_buffer[0] != '\0') {
	  ClearLine(LINES-15);
	  Centerline(LINES-15, central_message_buffer);
	  fflush(stdout);
	}
}

clear_central_message()
{
	/** clear the central message buffer **/

	central_message_buffer[0] = '\0';
}
END-OF-FILE

size=`wc -c < src/output_utils.c`

if [ $size != 1861 ]
then
  echo Warning: src/output_utils.c changed - should be 1861 bytes, not $size bytes
fi

chmod 644 src/output_utils.c

# ---------- file src/pattern.c ----------


if [ -f src/pattern.c ]
then
  echo File 'src/pattern.c' already exists\!
  exit 1
fi

echo extracting file src/pattern.c...
cat << 'END-OF-FILE' > src/pattern.c
/**			pattern.c			**/

/**    General pattern matching for the MSG mailer.     

       (C) Copyright 1986 Dave Taylor
**/

#include <errno.h>

#include "headers.h"

static char pattern[SLEN] = { "" };
static char alt_pattern[SLEN] = { "" };

extern int errno;

int
pattern_match()
{
	/** Get a pattern from the user and try to match it with the
	    from/subject lines being displayed.  If matched (ignoring
	    case), move current message pointer to that message, if
	    not, error and return ZERO **/

	register int i;

	PutLine(LINES-3,40,"/ =match anywhere in messages");
	
	PutLine(LINES-1,0, "Match Pattern:");

	if (pattern_enter(pattern, alt_pattern, LINES-1, 16, 
	    "Match Pattern (in entire mailbox):"))
	  if (strlen(alt_pattern) > 0)
	    return(match_in_message(alt_pattern));
	  else
	    return(1);
	  
	if (strlen(pattern) == 0) 
	  return(0);

	for (i = current; i < message_count; i++) {
	  if (from_matches(i, pattern)) {
	    current = ++i;
	    return(1);
	  }
	  else if (subject_matches(i, pattern)) {
	    current = ++i;
	    return(1);
	  }
	}

	return(0);
}

int
from_matches(message_number, pattern)
int message_number;
char *pattern;
{
	/** Returns true iff the pattern occurs in it's entirety
	    in the from line of the indicated message **/

	
	return( in_string(header_table[message_number].from, pattern) );
}

int
subject_matches(message_number, pattern)
int message_number;
char *pattern;
{
	/** Returns true iff the pattern occurs in it's entirety
	    in the subject line of the indicated message **/

	
	return( in_string(header_table[message_number].subject, pattern) );
}

match_in_message(pattern)
char *pattern;
{
	/** Match a string INSIDE a message...starting at the current 
	    message read each line and try to find the pattern.  As
	    soon as we do, set current and leave! 
	    Returns 1 if found, 0 if not
	**/

	char buffer[LONG_STRING];
	int  message_number;
	long location;

	dprint1("match_in_message(pattern='%s')\n", pattern);

	location = header_table[current-1].offset;
	message_number = current-1;

	if (fseek(mailfile, location, 0) != 0) {

	  dprint2("\tfseek(mailfile, %ld, 0) failed with error %d\n",
		   location, errno);

	  error1("msg [match] failed looking %ld bytes into file",
		 location);
	  return(1);	/* fake it out to avoid replacing error message */
	}

	error("searching for pattern...");

	while (fgets(buffer, LONG_STRING, mailfile) != NULL) {
	
	  if (in_string(buffer, pattern)) {
	    current = message_number; 
	    clear_error();
	    return(1);
	  }

	  location += strlen(buffer);

	  if (location > header_table[message_number].offset)
	    message_number++;
	}

	return(0);
}
END-OF-FILE

size=`wc -c < src/pattern.c`

if [ $size != 2675 ]
then
  echo Warning: src/pattern.c changed - should be 2675 bytes, not $size bytes
fi

chmod 644 src/pattern.c

# ---------- file src/quit.c ----------


if [ -f src/quit.c ]
then
  echo File 'src/quit.c' already exists\!
  exit 1
fi

echo extracting file src/quit.c...
cat << 'END-OF-FILE' > src/quit.c
/**		quit.c		**/

/** quit: leave the current mailbox and quit the program.
  
    (C) Copyright 1985, Dave Taylor
**/

#ifndef TRUE
#define TRUE	1
#endif

quit()
{
	/* a wonderfully short routine!! */

	leave_mbox();

	leave();
}

resync()
{
	/* leave the current mailbox and read it in again.  This
	   is used as needed to allow editing of messages and so
	   on... */

	leave_mbox();

	error("reading mailfile in again...");

	newmbox(2, TRUE);
	showscreen();
}
END-OF-FILE

size=`wc -c < src/quit.c`

if [ $size != 466 ]
then
  echo Warning: src/quit.c changed - should be 466 bytes, not $size bytes
fi

chmod 644 src/quit.c

# ---------- file src/read_rc.c ----------


if [ -f src/read_rc.c ]
then
  echo File 'src/read_rc.c' already exists\!
  exit 1
fi

echo extracting file src/read_rc.c...
cat << 'END-OF-FILE' > src/read_rc.c
/**			read_rc.c			**/

/** (C) Copyright 1985, Dave Taylor			**/

/** This file contains programs to allow the user to have a .msgrc file
    in their home directory containing any of the following: 

	fullname= <username string>
	maildir = <directory>
	mailbox = <file>
	editor  = <editor>
	savemail= <savefile>
	shell   = <shell>
	print   = <print command>
	weedout = <list of headers to weed out>
	prefix  = <copied message prefix string>

	alternatives = <list of addresses that forward to us>

    and/or the logical arguments:
	
	autocopy [on|off]
	copy     [on|off]	
	resolve  [on|off]
	weed     [on|off]
	noheader [on|off]
	titles   [on|off]
	editout  [on|off]
	savebyname [on|off]
	movepage [on|off]

    Lines starting with '#' are considered comments and are not checked
    any further!

    Modified 10/85 to know about "Environment" variables..
    Modified 12/85 for the 'prefix' option
    Modified  2/86 for the "movepage" flag
**/

#include <stdio.h>
#include <ctype.h>

#ifdef BSD
#undef tolower
#endif

#include "headers.h"

char *shift_lower(), *strtok(), *getenv();

#define NOTWEEDOUT	0
#define WEEDOUT		1
#define ALTERNATIVES	2

read_rc_file()
{
	/** this routine does all the actual work of reading in the
	    .rc file... **/

	FILE *file;
	char buffer[SLEN], filename[SLEN];
	char word1[SLEN], word2[SLEN];
	int  errors = 0, last = NOTWEEDOUT;

	dprint0("read_rc_file()\n");
	
	sprintf(filename,"%s/%s", home, msgrcfile);

	default_weedlist();

	alternative_addresses = NULL; 	/* none yet! */

	if ((file = fopen(filename, "r")) == NULL)
	  return;	/* we're done! */

	while (fgets(buffer, SLEN, file) != NULL) {
	  no_ret(buffer);	 	/* remove return */
	  if (buffer[0] == '#') { 	/* comment       */
	    last = NOTWEEDOUT;
	    continue;
	  }
	  if (strlen(buffer) < 2) {	/* empty line    */
	    last = NOTWEEDOUT;
	    continue;
	  }

	  breakup(buffer, word1, word2);

	  strcpy(word1, shift_lower(word1));	/* to lower case */

	  if (word2[0] == 0 && (last != WEEDOUT || last != ALTERNATIVES)) {
	    fprintf(stderr, "Malformed line from .msgrc file: %s\n", buffer);
	    errors++;
	    continue;
	  }
	
	  if (equal(word1,"maildir") || equal(word1,"folders")) {
	    expand_env(folders, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "fullname") || equal(word1,"username") ||
		   equal(word1, "name")) {
	    strcpy(full_username, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "prefix")) {
	    strcpy(prefixchars, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "shell")) {
	    expand_env(shell, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "mailbox")) {
	    expand_env(mailbox, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "editor") || equal(word1,"mailedit")) {
	    expand_env(editor, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "savemail") || equal(word1, "saveto")) {
	    expand_env(savefile, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "print") || equal(word1, "printmail")) {
	    expand_env(printout, word2);
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "autocopy")) {
	    auto_copy = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "copy") || equal(word1, "auto_cc")) {
	    auto_cc = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "resolve")) {
	    resolve_mode = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "weed")) {
	    filter = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "noheader")) {
	    noheader = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "titles")) {
	    title_messages = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "editout")) {
	    edit_outbound = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "savebyname") || equal(word1, "savename")) {
	    save_by_name = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "movepage") || equal(word1, "page") ||
		   equal(word1, "movewhenpaged")) {
	    move_when_paged = equal(shift_lower(word2), "on");
	    last = NOTWEEDOUT;
	  }
	  else if (equal(word1, "weedout")) {
	    weedout(word2);
	    last = WEEDOUT;
	  }
	  else if (equal(word1, "alternatives")) {
	    alternatives(word2);
	    last = ALTERNATIVES;
	  }
	  else if (last == WEEDOUT)	/* could be multiple line weedout */
	    weedout(buffer);
	  else if (last == ALTERNATIVES)	/* multi-line addresses   */
	    alternatives(buffer);
	  else {
	    fprintf(stderr, "Unknown line from .rc file: %s\n", buffer);
	    errors++;
	  }
	}

	if (errors) {
	  fprintf(stderr,"Msg quit from errors in .msgrc file\n");
	  exit(errors);
	}
}
	
weedout(string)
char *string;
{
	/** This routine is called with a list of headers to weed out.   **/

	register int i;
	char *strptr, *header;

	dprint1("weedout(string='%s')\n", string);

	strptr = (char *) string;

	while ((header = strtok(strptr, "\t ,\"'")) != NULL) {
	  if (weedcount == MAX_IN_WEEDLIST)
	    exit(fprintf(stderr, "Read in too many weed headers.  Max is %d\n", 
		   MAX_IN_WEEDLIST-1));
	  if (strlen(header) > 0)
	    if (strlen(header) > NLEN)
	      fprintf(stderr, "Bad weed header: %s.  Too Long - Len is < %d!\n",
	             header, NLEN);
	    else {
	      for (i=0; i < strlen(header); i++)
	        if (header[i] == '_') header[i] = ' ';
	      strncpy(weedlist[weedcount++], header, NLEN);
	    }
	  strptr = (char *) NULL;
	}
}

alternatives(string)
char *string;
{
	/** This routine is called with a list of alternative addresses
	    that you may receive mail from (forwarded) **/

	char *strptr, *address;
	struct addr_rec *current_record, *previous_record;

	dprint1("alternatives(string='%s')\n", string);

	previous_record = NULL;

	strptr = (char *) string;

	while ((address = strtok(strptr, "\t ,\"'")) != NULL) {
	  if (previous_record == NULL) {
	    previous_record = (struct addr_rec *) 
			       malloc(sizeof *alternative_addresses);

	    strcpy(previous_record->address, address);
	    previous_record->next = NULL;
	    alternative_addresses = previous_record;
	  }
	  else {
	    current_record = (struct addr_rec *) 
			       malloc(sizeof *alternative_addresses);
	  
	    strcpy(current_record->address, address);
	    current_record->next = NULL;
	    previous_record->next = current_record;
	    previous_record = current_record;
	  }
	  strptr = (char *) NULL;
	}
}

default_weedlist()
{
	/** install the default headers to weed out! **/

	dprint0("default_weedlist()\n");

	weedcount = 0;

	strncpy(weedlist[weedcount++], ">From", NLEN);
	strncpy(weedlist[weedcount++], "In-Reply-To", NLEN);
	strncpy(weedlist[weedcount++], "References", NLEN);
	strncpy(weedlist[weedcount++], "Newsgroups", NLEN);
	strncpy(weedlist[weedcount++], "Received", NLEN);
	strncpy(weedlist[weedcount++], "Apparently-To:", NLEN);
	strncpy(weedlist[weedcount++], "Message-Id:", NLEN);
}

int
matches_weedlist(buffer)
char *buffer;
{
	/** returns true iff the first 'n' characters of 'buffer' 
	    match an entry of the weedlist **/
	
	register int i;

	dprint1("matches_weedlist(buffer='%s')\n", buffer);

	for (i=0;i < weedcount; i++)
	  if (strncmp(buffer, weedlist[i], strlen(weedlist[i])) == 0) 
	    return(1);

	return(0);
}

breakup(buffer, word1, word2)
char *buffer, *word1, *word2;
{
	/** This routine breaks buffer down into word1, word2 where 
	    word1 is alpha characters only, and there is an equal
	    sign delimiting the two...
		alpha = beta
	    For lines with more than one 'rhs', word2 is set to the
	    entire string...
	**/

	register int i;
	
	for (i=0;i<strlen(buffer) && isalpha(buffer[i]); i++)
	  word1[i] = buffer[i];

	word1[i++] = '\0';	/* that's the first word! */

	/** spaces before equal sign? **/

	while (buffer[i] == ' ' || buffer[i] == '\t') i++;

	if (buffer[i] == '=') i++;

	/** spaces after equal sign? **/

	while (buffer[i] == ' ' || buffer[i] == '\t') i++;

	if (i < strlen(buffer))
	  strcpy(word2, (char *) (buffer + i));
	else
	  word2[0] = '\0';
}

expand_env(dest, buffer)
char *dest, *buffer;
{
	/** expand possible metacharacters in buffer and then copy
	    to dest... 
	    This routine knows about "~" being the home directory,
	    and "$xxx" being an environment variable.
	**/

	char  *word, *string, next_word[SLEN];
	
	if (buffer[0] == '/') {
	  dest[0] = '/';
	  dest[1] = '\0';
	}
	else
	  dest[0] = '\0';
	string = (char *) buffer;

	while ((word = strtok(string, "/")) != NULL) {
	  if (word[0] == '$') {
	    strcpy(next_word, getenv((char *) (word + 1)));
	    if (strlen(next_word) == 0)
	      leave(printf("\n\rCan't expand environment variable '%s'\n\r",
		    word));
	  }
	  else if (word[0] == '~' && word[1] == '\0')
	    strcpy(next_word, home);
	  else
	    strcpy(next_word, word);

	  sprintf(dest, "%s%s%s", dest, (strlen(dest) > 0? "/":""),
		          next_word);

	  string = (char *) NULL;
	}
}
END-OF-FILE

size=`wc -c < src/read_rc.c`

if [ $size != 9083 ]
then
  echo Warning: src/read_rc.c changed - should be 9083 bytes, not $size bytes
fi

chmod 644 src/read_rc.c

# ---------- file src/reply.c ----------


if [ -f src/reply.c ]
then
  echo File 'src/reply.c' already exists\!
  exit 1
fi

echo extracting file src/reply.c...
cat << 'END-OF-FILE' > src/reply.c
/**		reply.c		**/

/*** routine allows replying to the sender of the current message 

     (C) Copyright 1985, Dave Taylor
***/

#include "headers.h"
#ifndef BSD
#  include <sys/utsname.h>
#endif

/** Note that this routine generates automatic header information
    for the subject and (obviously) to lines, but that these can
    be altered while in the editor composing the reply message! 
**/

char *strip_parens(), *get_token(), *notes_machine();

int
reply()
{
	/** Reply to the current message.  Returns non-zero iff
	    the screen has to be rewritten. **/

	char return_address[LONG_SLEN], subject[SLEN];
	int  return_value;

	dprint0("reply()\n");

	get_return(return_address);
	
	if (header_table[current-1].subject[0] != '\0') {
	  if ((strncmp("Re:", header_table[current-1].subject, 3) == 0) ||
	      (strncmp("RE:", header_table[current-1].subject, 3) == 0) ||
	      (strncmp("re:", header_table[current-1].subject, 3) == 0)) 
	    strcpy(subject, header_table[current-1].subject);
	  else {
	    strcpy(subject,"Re: ");
	    strcat(subject,header_table[current-1].subject); 
	  }
	  return_value = send(return_address, subject, TRUE);
	}
	else
	  return_value = send(return_address, "Re: your mail", TRUE);

	return(return_value);
}

int
reply_to_everyone()
{
	/** Reply to everyone who received the current message.  
	    This includes other people in the 'To:' line and people
	    in the 'Cc:' line too.  Returns non-zero iff the screen 
            has to be rewritten. **/

	char return_address[LONG_SLEN], subject[SLEN];
	char full_address[VERY_LONG_STRING];
	int  return_value;

	dprint0("reply_to_everyone()\n");

	get_return(return_address);

	strcpy(full_address, return_address);	/* sender gets copy */
	
	get_and_expand_everyone(return_address, full_address);

	if (header_table[current-1].subject[0] != '\0') {
	  if ((strncmp("Re:", header_table[current-1].subject, 3) == 0) ||
	      (strncmp("RE:", header_table[current-1].subject, 3) == 0) ||
	      (strncmp("re:", header_table[current-1].subject, 3) == 0)) 
	    strcpy(subject, header_table[current-1].subject);
	  else {
	    strcpy(subject,"Re: ");
	    strcat(subject,header_table[current-1].subject); 
	  }
	  return_value = send(full_address, subject, TRUE);
	}
	else
	  return_value = send(full_address, "Re: your mail", TRUE);

	return(return_value);

}

int
forward()
{
	/** Forward the current message.  What this actually does is
	    to set auto_copy to true, then call 'send' to get the 
	    address and route the mail. 
	**/

	char subject[SLEN], address[VERY_LONG_STRING];
	int  original_cc, results, edit_msg;

	dprint0("forward()\n");

	original_cc = auto_copy;
	address[0] = '\0';

	edit_msg = (want_to("Edit outgoing message (y/n) ? ",'y',FALSE) != 'n');
	printf("%s", edit_msg? "Yes" : "No");

	auto_cc = TRUE;			/* we want a copy */

	if (strlen(header_table[current-1].subject) > 0) {
	  strcpy(subject,header_table[current-1].subject); 
	  results = send(address, subject, edit_msg);
	}
	else
	  results = send(address, "Forwarded Mail...", edit_msg);
	
	auto_copy = original_cc;

	return(results);
}

get_and_expand_everyone(return_address, full_address)
char *return_address, *full_address;
{
	/** Read the current message, extracting addresses from the 'To:'
	    and 'Cc:' lines.   As each address is taken, ensure that it
	    isn't to the author of the message NOR to us.  If neither,
	    prepend with current return address and append to the 
	    'full_address' string.
	**/

    char ret_address[LONG_SLEN], buf[LONG_SLEN], new_address[LONG_SLEN];
    char *bufptr, *address;
    int  in_message = 1, first_pass = 0;

    dprint1("get_and_expand_everyone(return_address='%s', <buffer>)\n", 
	     return_address);

    /** First off, get to the first line of the message desired **/

    if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
	error1("msg [seek] couldn't read %d bytes into file",
	       header_table[current-1].offset);
	return;
    }
 
    /** okay!  Now we're there!  **/

    /** let's fix the ret_address to reflect the return address of this
	message with '%s' instead of the persons login name... **/

    translate_return(return_address, ret_address);

    /** now let's parse the actual message! **/

    while (in_message) {
      in_message = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL);
      if (first_word(buf, "From ") && first_pass++ != 0) 
	in_message = FALSE;
      else if (first_word(buf, "To:") || first_word(buf, "Cc:") ||
	       first_word(buf, "CC:") || first_word(buf, "cc:")) {
	do {
	  no_ret(buf);

	  bufptr = (char *) (strip_parens(buf) + 3); /* 3 = strlen of prompt */

	  while ((address = get_token(bufptr, "\t, ", 0)) != NULL) {
	    if (okay_address(address, return_address)) {
	      sprintf(new_address, ret_address, address);
	      optimize_and_add(new_address, full_address);
	    }
	    bufptr = NULL;
	  }

          in_message = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL);
	
	} while (in_message && whitespace(buf[0]));

      }
      else if (strlen(buf) < 2)	/* done with header */
	 in_message = FALSE;
    }
}

int
okay_address(address, return_address)
char *address, *return_address;
{
	/** This routine checks to ensure that the address we just got
	    from the "To:" or "Cc:" line isn't us AND isn't the person	
	    who sent the message.  Returns true iff neither is the case **/

	char our_address[SLEN];
	struct addr_rec  *alternatives;

	if (in_string(address, return_address))
	  return(FALSE);

	sprintf(our_address, "%s!%s", hostname, username);

	if (in_string(address, our_address))
	  return(FALSE);

	sprintf(our_address, "%s@%s", username, hostname);
	  
	if (in_string(address, our_address))
	  return(FALSE);

	alternatives = alternative_addresses;

	while (alternatives != NULL) {
	  if (in_string(address, alternatives->address))
	    return(FALSE);
	  alternatives = alternatives->next;
	}

	return(TRUE);
}
	    
optimize_and_add(new_address, full_address)
char *new_address, *full_address;
{
	/** This routine will add the new address to the list of addresses
	    in the full address buffer IFF it doesn't already occur.  It
	    will also try to fix dumb hops if possible, specifically hops
	    of the form ...a!b...!a... and hops of the form a@b@b etc 
	**/

	register int len, host_count = 0, i;
	char     hosts[MAX_HOPS][SLEN];	/* array of machine names */
	char     *host, *addrptr;

	if (in_string(full_address, new_address))
	  return(1);	/* duplicate address */

	/** optimize **/
	/*  break down into a list of machine names, checking as we go along */
	
	addrptr = (char *) new_address;

	while ((host = get_token(addrptr, "!", 1)) != NULL) {
	  for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
	      ;

	  if (i == host_count) {
	    strcpy(hosts[host_count++], host);
	    if (host_count == MAX_HOPS) {
	       error("Can't build return address - hit MAX_HOPS limit!");
	       return(1);
	    }
	  }
	  else 
	    host_count = i + 1;
	  addrptr = NULL;
	}

	/** fix the ARPA addresses, if needed **/
	
	if (chloc(hosts[host_count-1], '@') > -1)
	  fix_arpa_address(hosts[host_count-1]);
	  
	/** rebuild the address.. **/

	new_address[0] = '\0';

	for (i = 0; i < host_count; i++)
	  sprintf(new_address, "%s%s%s", new_address, 
	          new_address[0] == '\0'? "" : "!",
	          hosts[i]);

	dprint1("\tgenerated address '%s'\n", new_address);

	if (full_address[0] == '\0')
	  strcpy(full_address, new_address);
	else {
	  len = strlen(full_address);
	  full_address[len  ] = ',';
	  full_address[len+1] = ' ';
	  full_address[len+2] = '\0';
	  strcat(full_address, new_address);
	}

	return(0);
}

get_return_name(address, name)
char *address, *name;
{
	/** Given the address (either a single address or a combined list 
	    of addresses) extract the login name of the first person on
	    the list and return it as 'name'.  Modified to stop at
	    any non-alphanumeric character. **/

	/** An important note to remember is that it isn't vital that this
	    always returns just the login name, but rather that it always
	    returns the SAME name.  If the persons' login happens to be,
	    for example, joe.richards, then it's arguable if the name 
	    should be joe, or the full login.  It's really immaterial, as
	    indicated before, so long as we ALWAYS return the same name! **/

	/** Another note: modified to return the argument as all lowercase
	    always... **/

	char single_address[LONG_SLEN];
	register int i, loc, index = 0;

	/* first step - copy address up to a comma, space, or EOLN */

	for (i=0; address[i] != ',' && ! whitespace(address[i]) && 
	     address[i] != '\0'; i++)
	  single_address[i] = address[i];
	single_address[i] = '\0';

	/* Now is it an ARPA address?? */

	if ((loc = chloc(single_address, '@')) != -1) {	  /* Yes */

	  /* At this point the algorithm is to keep shifting our copy 
	     window left until we hit a '!'.  The login name is then
             located between the '!' and the first metacharacter to 
	     it's right (ie '%', ':' or '@'). */

	  for (i=loc; single_address[i] != '!' && i > -1; i--)
	      if (single_address[i] == '%' || 
	          single_address[i] == ':' ||
	          single_address[i] == '.' ||	/* no domains */
		  single_address[i] == '@') loc = i-1;
	
	  if (single_address[i] == '!') i++;

	  for (index = 0; index < loc - i + 1; index++)
	    name[index] = tolower(single_address[index+i]);
	  name[index] = '\0';
	}
	else {	/* easier - standard USENET address */

	  /* This really is easier - we just cruise left from the end of
	     the string until we hit either a '!' or the beginning of the
             line.  No sweat. */

	  loc = strlen(single_address)-1; 	/* last char */

	  for (i = loc; single_address[i] != '!' && single_address[i] != '.' 
	       && i > -1; i--)
	     name[index++] = tolower(single_address[i]);
	  name[index] = '\0';
	  reverse(name);
	}
}
END-OF-FILE

size=`wc -c < src/reply.c`

if [ $size != 9927 ]
then
  echo Warning: src/reply.c changed - should be 9927 bytes, not $size bytes
fi

chmod 644 src/reply.c

# ---------- file src/return_addr.c ----------


if [ -f src/return_addr.c ]
then
  echo File 'src/return_addr.c' already exists\!
  exit 1
fi

echo extracting file src/return_addr.c...
cat << 'END-OF-FILE' > src/return_addr.c
/**			return_addr.c			**/

/** This set of routines is used to generate real return addresses
    and also return addresses suitable for inclusion in a users
    alias files (ie optimized based on the pathalias database).

    These routines (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"

#include <sys/types.h>
#include <sys/stat.h>

char *shift_lower(), *notes_machine(), *expand_address();

optimize_return(address)
char *address;
{
	/** This routine tries to create an optimized address, that is,
	    an address that has the minimal information needed to 
	    route a message to this person given the current path
	    database...
	**/

	/** first step is to figure out what sort of address we 
	    have... **/

	dprint1("\noptimize_return(address='%s')\n", address);

	if (chloc(address, '%') != -1)
	  optimize_cmplx_arpa(address);
	else if (chloc(address, '@') != -1)
	  optimize_arpa(address);
	else
	  optimize_usenet(address);
}

optimize_cmplx_arpa(address)
char *address;
{
	/** Try to optimize a complex ARPA address.  A Complex address is one 
	    that contains '%' (deferred '@').  For example:  
		veeger!hpcnof!hplabs!joe%sytech@syte  
	    is a complex address (no kidding, right?).  The algorithm for 
	    trying to resolve it is to move all the way to the right, then 
	    back up left until the first '!' then from there to the SECOND 
	    metacharacter on the right is the name@host address...(in this 
            example, it would be "joe%sytech").  Check this in the routing
	    table.  If not present, keep backing out to the right until we
	    find a host that is present, or we hit the '@' sign.  Once we
	    have a 'normal' ARPA address, hand it to optimize_arpa().
	**/

	char name[SHORT_SLEN], buffer[SLEN], junk[SLEN];
	char host[SHORT_SLEN], old_host[SHORT_SLEN];
	register int i, loc, nloc = 0, hloc = 0, passes = 1;

	dprint1("optimize_cmplx_arpa(address='%s')\n", address);

	/** first off, get the name%host... **/

	for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--)
	   ;

	while (address[loc] != '\0') {

	  if (passes == 1) {
	    loc++;

	    while (address[loc] != '%' && address[loc] != '@')
	      name[nloc++] = address[loc++];
	  }
	  else {
	    for (i=0; old_host[i] != '\0'; i++)
	      name[nloc++] = old_host[i];
	  }

	  loc++;
  
	  while (address[loc] != '%' && address[loc] != '@')
	    host[hloc++] = address[loc++];
  
	  host[hloc] = name[nloc] = '\0';

	  dprint2("\tgot name = %s and host = %s\n", name, host);

	  strcpy(old_host, host);
	  remove_domains(host);

	  sprintf(buffer, "%s@%s", name, shift_lower(host));

	  if (expand_site(buffer, junk) == 0) {
	    strcpy(address, buffer);
	    return;
	  }
	  else if (address[loc] == '@') {
	    optimize_arpa(address);
	    return;
	  }
	  else
	    name[nloc++] = '%';	/* for next pass through */

	}
}

optimize_arpa(address)
char *address;
{
	/** Get an arpa address and simplify it to the minimal
	    route needed to get mail to this person... **/

	char name[SHORT_SLEN], buffer[SLEN], junk[SLEN];
	char host[SHORT_SLEN];
	register int loc, nloc = 0, hloc = 0, at_sign = 0;

	dprint1("optimize_arpa(address='%s')\n", address);

	for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) {
	  if (address[loc] == '@')
	     at_sign++;	/* remember this spot! */
	  else if (at_sign)
	    name[nloc++] = address[loc];
	  else
	    host[hloc++] = address[loc];
	}

	name[nloc] = host[hloc] = '\0';

	reverse(name);
	reverse(host);

	remove_domains(host);

	dprint2("\tname = %s and host = %s\n", name, shift_lower(host));

	sprintf(buffer,"%s@%s", name, shift_lower(host));

	if (expand_site(buffer, junk) == 0) {
	  strcpy(address, buffer);
	  return;
	}

	optimize_usenet(address);	/* that didn't work... */
}
	
optimize_usenet(address)
char *address;
{
	/** optimize the return address IFF it's a standard usenet
	    address...
	**/

	char name[SHORT_SLEN],  new_address[SLEN], buffer[SLEN], junk[SLEN];
	register int loc, nloc = 0, aloc = 0, passes = 1;

	dprint1("\noptimize_usenet(address='%s')\n", address);

	for (loc = strlen(address)-1; address[loc] != '!' && loc > -1; loc--) 
	  name[nloc++] = address[loc];
	name[nloc] = '\0';

	reverse(name);

	dprint1("\tgot name = %s\n", name);

	new_address[0] = '\0';	

	/* got name, now get machine until we can get outta here */

	while (loc > -1) {

	  new_address[aloc++] = address[loc--];	/* the '!' char */

	  while (address[loc] != '!' && loc > -1)
	    new_address[aloc++] = address[loc--];

	  new_address[aloc] = '\0';

	  strcpy(buffer, new_address);
	  reverse(buffer);
	
	  if (expand_site(buffer, junk) == 0) {
	    if (passes == 1 && chloc(name, '@') == -1) {
	      buffer[strlen(buffer) - 1] = '\0';	/* remove '!' */
	      dprint2("\tReturning address %s@%s\n", name, buffer);
	      sprintf(address, "%s@%s", name, buffer);
	    }
	    else {
	      dprint2("\tReturning address '%s%s'\n", buffer, name);
	      sprintf(address, "%s%s", buffer, name);
	    }
	    return;		/* success! */
	  }
	  else
	    dprint1("\taddress %s failed!\n", buffer);

	  passes++;
	}

	dprint0("\tnothing to do!\n");
	return;		/* nothing to do! */
}

get_return(buffer)
char *buffer;
{
	/** reads 'current' message again, building up the full return 
	    address including all machines that might have forwarded 
	    the message.  **/

    char buf[LONG_SLEN], name1[SLEN], name2[SLEN], lastname[SLEN];
    char hold_return[LONG_SLEN], alt_name2[SLEN];
    int ok = 1, lines;

    dprint0("get_return(<buffer>)\n");

    /** are we reading a notesfile file??  **/

    if (notesfile) {
      strcpy(buf, header_table[current-1].from);
      if (chloc(buf, '!') == -1) 
        sprintf(buf, "%s!%s", notes_machine(), header_table[current-1].from);
      strcpy(buffer, expand_system(buf, 1));
      dprint1("\treturn address [notes] = %s\n", buffer);
      return;
    }

    /** get to the first line of the message desired **/

    if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
	error1("msg [seek] couldn't read %d bytes into file",
	       header_table[current-1].offset);
	return;
    }
 
    /** okay!  Now we're there!  **/

    lines = header_table[current-1].lines;
    
    buffer[0] = '\0';

    while (ok && lines--) {
      ok = (int) (fgets(buf, LONG_SLEN, mailfile) != NULL);
      if (first_word(buf, "From ")) {
	sscanf(buf, "%*s %s", hold_return);
      }
      else if (first_word(buf, ">From")) {
	sscanf(buf,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s %s", 
	       name1, name2, alt_name2);
	if (strcmp(name2, "from") == 0)
	  strcpy(name2, alt_name2);
	add_site(buffer, name2, lastname);
      }

#ifdef USE_EMBEDDED_ADDRESSES

      else if (first_word(buf, "From:")) {
	get_address_from("From:", buf, hold_return);
      }
      else if (first_word(buf, "Reply-To:")) {
	get_address_from("Reply-To:", buf, buffer);
	return;
      }

#endif

      else if (strlen(buf) < 2)	/* done with header */
         lines = 0; /* let's get outta here!  We're done!!! */
     }

    if (buffer[0] == '\0')
      strcpy(buffer, hold_return); /* default address! */
    else
      add_site(buffer, name1, lastname);	/* get the user name too! */

    dprint1("\treturn address = '%s'\n", buffer);

}
END-OF-FILE

size=`wc -c < src/return_addr.c`

if [ $size != 7247 ]
then
  echo Warning: src/return_addr.c changed - should be 7247 bytes, not $size bytes
fi

chmod 644 src/return_addr.c

# ---------- file src/savecopy.c ----------


if [ -f src/savecopy.c ]
then
  echo File 'src/savecopy.c' already exists\!
  exit 1
fi

echo extracting file src/savecopy.c...
cat << 'END-OF-FILE' > src/savecopy.c
/** 			savecopy.c			**/

/** Save a copy of the specified message in the users savemail mailbox.

         (C) Copyright 1986, Dave Taylor 			       
**/

#include "headers.h"
#ifdef BSD
#  include <sys/time.h>
#else
#  include <time.h>
#endif

char *format_long(), *get_arpa_date();

extern char reply_to[SLEN];	/* In-Reply-To: string */
extern int gotten_key;		/* for encryption      */

save_copy(subject, to, cc, filename, original_to)
char *subject, *to, *cc, *filename, *original_to;
{
	/** This routine appends a copy of the outgoing message to the
	    file specified by the SAVEFILE environment variable.  **/

	FILE *save,		/* file id for file to save to */
	     *message;		/* the actual message body     */
	long  thetime;		/* variable holder for time    */
	char  buffer[SLEN],	/* read buffer 		       */
	      savename[SLEN],	/* name of file saving into    */
	      newbuffer[SLEN];  /* first name in 'to' line     */
	register int i;		/* for chopping 'to' line up   */
	int   crypted=0;	/* are we encrypting?          */

	dprint5(
"save_copy(subject='%s', to='%s', cc='%s', filename='%s', original-to='%s')\n", 
		 subject, to, cc, filename, original_to);

	savename[0] = '\0';

	if (save_by_name) {
	  get_return_name(to, buffer);
	  sprintf(savename, "%s%s%s", folders, 
	          lastch(folders) == '/'? "" : "/", buffer);
	  if (access(savename, ACCESS_EXISTS) == -1) 	/* don't create new! */
	    savename[0] = '\0';
	}

	if (strlen(savename) == 0) {
	  if (strlen(savefile) == 0)
	    return(error("variable 'SAVEFILE' not defined!"));
	  strcpy(savename, savefile);
	  if (save_by_name)
	    error2("Not creating file '%s'...saving in %s instead",
	  	   buffer, savename);
	}

	dprint1("\tsaving copy of outbound message to file '%s'\n", savename);

	if ((save = fopen(savename, "a")) == NULL)
	  return(error1("couldn't append to %s", savename));

	if ((message = fopen(filename, "r")) == NULL) {
	  fclose(save);
	  return(error1("save_copy couldn't read file %s!", filename));
	}

	for (i=0; original_to[i] != '\0' && ! whitespace(original_to[i]); i++)
	  newbuffer[i] = original_to[i];

	newbuffer[i] = '\0';

	tail_of(newbuffer, buffer, FALSE);

	thetime = time(0);	/* this must be here for it to work! */

	fprintf(save,"\nFrom To:%s %s", buffer, (char *) ctime(&thetime));

	fprintf(save, "Date: %s\n", get_arpa_date());
			
	fprintf(save,"To: %s\nSubject: %s\n", 
		format_long(to,strlen("To: ")), subject);

	if (strlen(cc) > 0)
	  fprintf(save,"Cc: %s\n", 
		  format_long(cc, strlen("Cc:")));

	if (strlen(reply_to) > 0)
	  fprintf(save, "In-Reply-To: %s\n", reply_to);

	(void) putc('\n', save);	/* put another return, please! */

	/** now copy over the message... **/

	while (fgets(buffer, SLEN, message) != NULL) {
	  if (buffer[0] == '[') {
	    if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0)
	      crypted = 1;
	    else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0)
	      crypted = 0;
	    else if (strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0) {
	      fclose(message);
	      fclose(save);
	      chown(savename, userid, getgid());	
	      return(TRUE);
	    }
	  }
	  else if (crypted) {
	    if (! gotten_key++)
	      getkey(ON);
	    encode(buffer);
	  }
	  fputs(buffer, save);
	}

	dprint0("\tclosing the files...\n");

	fclose(message);
	fclose(save);

	/* make sure save file isn't owned by root! */
	chown(savename, userid, getgid());	

	return(TRUE);
}
END-OF-FILE

size=`wc -c < src/savecopy.c`

if [ $size != 3441 ]
then
  echo Warning: src/savecopy.c changed - should be 3441 bytes, not $size bytes
fi

chmod 644 src/savecopy.c

# ---------- file src/screen.c ----------


if [ -f src/screen.c ]
then
  echo File 'src/screen.c' already exists\!
  exit 1
fi

echo extracting file src/screen.c...
cat << 'END-OF-FILE' > src/screen.c
/**		screen.c		**/

/**  screen display routines for MSG program 

     (C) Copyright 1985, Dave Taylor
**/

#include "headers.h"

static   int  last_current	 = -1;

showscreen()
{
	char buffer[SLEN];

	dprint0("showscreen()\n");

	ClearScreen();

	if (notesfile) 
	  sprintf(buffer, "Notes from '%s' (%d note%s) [Version %s]",
	        infile, message_count, 
		plural(message_count), VERSION);
	else 
	  sprintf(buffer, "Mailbox is '%s' with %d message%s [Version %s]",
	        infile, message_count, 
		plural(message_count), VERSION);
	Centerline(1, buffer);

	last_header_page = -1;	 	/* force a redraw regardless */
	show_headers();

	if (mini_menu)
	  show_menu();

	show_last_error();
	
	if (hp_terminal) 
	  define_softkeys(MAIN);
}

show_menu()
{
	/** write main system menu... **/

	Centerline(LINES-7,
  "|=pipe, !=shell, ?=help, <n>=set current to n, /=search pattern");
        Centerline(LINES-6,
  "A)lias, C)hange mailbox, D)elete, F)orward, G)roup reply, M)ail,"); 
	Centerline(LINES-5,
   "N)ext, P)rint, R)eply, S)ave to file, Q)uit, U)ndelete, or eX)it");
}

int
show_headers()
{
	/** display page of headers (10) if present.  First check to 
	    ensure that header_page is in bounds, fixing silently if not.
	    If out of bounds, return zero, else return non-zero **/

	register int first = 0, line = 4, last = 0, last_line;
	char newfrom[SLEN], buffer[SLEN];
	
	dprint0("show_headers()\n");

	if (fix_header_page())
	  return(FALSE);

	if (header_page == last_header_page) 	/* nothing to do! */
	  return(FALSE);

	/** compute last header to display **/

	first= header_page * headers_per_page;

	last = first + (headers_per_page - 1);

	if (last >= message_count) last = message_count-1;

	dprint2("\tdisplaying headers %d thru %d\n", first, last);

	/** okay, now let's show the header page! **/

	while (first <= last) {
	  tail_of(header_table[first].from, newfrom, TRUE); 
	  build_header_line(buffer, &header_table[first], first+1, newfrom);
	  PutLine(line,COLUMNS-80,"%s", buffer);	/* avoid '%' probs */
	  CleartoEOLN();
	  first++;
	  line++;	/* for clearing up in a sec... */
	}
	
	/* clear up the rest of the screen! */

	if (mini_menu)
	  last_line = LINES-8;
	else
	  last_line = LINES-3;

	while (line < last_line) {
	  MoveCursor(line,0);
	  CleartoEOLN();
	  line++;
	}

	display_central_message();

	last_current = current;
	last_header_page = header_page;

	return(TRUE);
}

show_current()
{
	/** display page of headers (10) if present.  First check to 
	    ensure that header_page is in bounds, fixing silently if not.
	    Note that this will ensure that 'current' is always set to
	    the top message on the screen if we go to a new screen! **/

	register int first = 0, last = 0, line = 4, changed;
	
	dprint0("show_current()\n");

	changed = fix_header_page();

	/** compute last header to display **/

	first = header_page * headers_per_page;
	last  = first + (headers_per_page - 1);

	if (last > message_count) 
	  last = message_count;

	/** okay, now let's show the pointers... **/

	/** have we changed??? **/

	if (current == last_current) {
	  dprint0("\tno change.  at same message!\n");
	  return;
	}

	/** first condition - current on this page & last too */

	if (last_current >= first && last_current <= last+1) {
	  dprint2("\tMoving arrow from %d to (current) %d\n",
	           last_current, current);
	  PutLine(((last_current-1) % headers_per_page) + 4, COLUMNS-76,"  ");
	  PutLine(((current-1) % headers_per_page) + 4, COLUMNS-76,"<-");
	}
	
	/** second condition - current on this page **/
	else if (! changed) {
	  dprint1("\tMoving arrow to (current) %d\n", current);
	  PutLine(((current-1) % headers_per_page) + 4, COLUMNS-76,"<-");
	}

	/** third condition - page changed!	    **/
	else {
          dprint2("\tdisplaying from message %d to %d\n", first, last);

	  while (first <= last) {
	    if (current-1 == first)   PutLine(line++,COLUMNS-76,"<-");
 	    else	  	      PutLine(line++,COLUMNS-76,"  ");
	    first++;
	  }
	}
	last_current = current;
}

build_header_line(buffer, entry, number, from)
char *buffer;
struct header_rec *entry;
int number;
char *from;
{
	/** Build in buffer the message header ... entry is the current
	    message entry, number is the numerical ID of the message,
	    and 'from' is a modified (displayable) from line... **/

	/** Note: using 'strncpy' allows us to output as much of the
	    subject line as possible given the dimensions of the screen.
	    The key is that 'strncpy' returns a 'char *' to the string
	    that it is handing to the dummy variable!  Neat, eh? **/
	
	char subj[LONG_SLEN];		/* to output subject */

	strncpy(subj, entry->subject, COLUMNS-34);

	subj[COLUMNS-34] = '\0';	/* insurance, eh? */

	sprintf(buffer, "%c%3d%s %c %3.3s %-2d %-18.18s  %s", 
	        (entry->priority? 'U' : new_msg(entry)? 'N' : ' '),
		number, (number == current? "<-" : "  "),
		(entry->delete? '*' : ' '), 
	        entry->month, atoi(entry->day), from, subj);
}

int
fix_header_page()
{
	/** this routine will check and ensure that the current header
	    page being displayed contains messages!  It will silently
	    fix 'header-page' if wrong.  Returns TRUE if changed.  **/

	int last_page, old_header;

	dprint1("fix_header_page() [%d messages total]\n", message_count);

	old_header = header_page;

	last_page = (int) ((message_count-1) / headers_per_page);
 
	dprint2("\tThere are 0 to %d pages in file [%d per page]\n", 
		last_page, headers_per_page);

	if (header_page > last_page) 
	  header_page = last_page;
	else if (header_page < 0) 
          header_page = 0;

	return(old_header != header_page);
}
END-OF-FILE

size=`wc -c < src/screen.c`

if [ $size != 5636 ]
then
  echo Warning: src/screen.c changed - should be 5636 bytes, not $size bytes
fi

chmod 644 src/screen.c

# ---------- file src/showmsg.c ----------


if [ -f src/showmsg.c ]
then
  echo File 'src/showmsg.c' already exists\!
  exit 1
fi

echo extracting file src/showmsg.c...
cat << 'END-OF-FILE' > src/showmsg.c
/** 		showmsg.c		**/

/** This file contains all the routines needed to display the specified
    message.

   These routines (C) Copyright 1986 Dave Taylor 
**/


#include "headers.h"
#include <ctype.h>

#ifdef BSD
#undef tolower
#endif

int
show_msg(number)
int number;
{
	/*** display number'th message.  Get starting and ending lines
	     of message from headers data structure, then fly through
	     the file, displaying only those lines that are between the
	     two!
	 	Returns non-zero if message shown!
	***/

	dprint1("show_msg(number=%d)\n", number);

	if (number > message_count) {
	  error1("Only %d messages!", message_count);
	  return(0);
	}
	else if (number < 1) {
	  error("you can't read THAT message!");
	  return(0);
	}

	return(show_message(header_table[number-1].lines, 
	       header_table[number-1].offset,number));
}

int
show_message(lines, file_loc, msgnumber)
int lines, msgnumber;
long file_loc;
{
	/*** Show the indicated range of lines from mailfile
	     for message 'msgnumber' by using 'display'
	     	Returns non-zero if it actually put something on 
	     the screen.
	***/

	dprint3("show_message(lines=%d, loc=%ld, msg-number=%d)\n", 
	        lines, file_loc, msgnumber);

	if (fseek(mailfile, file_loc, 0) != 0) {
	  error1("msg [seek] failed looking %d bytes into file",
		  file_loc);	
	  return(FALSE);
	}

	/* next read will get 'this' line so must be at end of previous */

	Raw(OFF);
	display(lines, msgnumber);
	Raw(ON);

	return(TRUE);	/* we did it boss! */
}
	

/** these two variables are used iff the variable 'title_message' is
    set, and are buffers for output of message title... **/

static char top_of_screen_left [LONG_STRING], 
     	    top_of_screen_right[LONG_STRING];

display(lines, msgnum)
int lines, msgnum;
{
	/** Display specified number of lines from file mailfile.
	    Note: This routine MUST be placed at the first line 
	    of the input file! **/

	char buffer[LONG_STRING], *full_month();

	int lines_on_screen = 0;		/* display */
	int crypted = 0, gotten_key = 0;	/* encryption */
	int weed_header, weeding_out = 0;	/* weeding */ 

	dprint2("display(lines=%d, msgnum=%d)\n", lines, msgnum);

	if (title_messages) {
	  tail_of(header_table[msgnum-1].from, buffer, FALSE);
	  sprintf(top_of_screen_left, "%s #%d %s %s", 
		   notesfile? "Note" : "Message", msgnum, 
		   (strncmp(header_table[msgnum-1].from, "To:", 3) == 0?
		    "to": "from"), buffer);
	  sprintf(top_of_screen_right," %s %s %s, %d at %s",
		  notesfile? "Posted" : "Mailed",
     		  full_month(header_table[msgnum-1].month), 
		  header_table[msgnum-1].day, 
	          atoi(header_table[msgnum-1].year) + 1900,
	          header_table[msgnum-1].time);

	  dprint1("\ttos_left: '%s'\n", top_of_screen_left);
	  dprint1("\ttos_right: '%s'\n", top_of_screen_right);
	}

	weed_header = filter;	/* allow us to change it after header */

	ClearScreen();

	if (cursor_control) transmit_functions(OFF);

	while (lines > 0) {

	    if (fgets(buffer, LONG_STRING, mailfile) == NULL) {
	      PutLine(LINES-1, 0, "Please press <space> to return: ");
	      (void) ReadCh();
	      if (cursor_control) transmit_functions(ON);
	      return(TRUE);
	    }

	    if (strlen(buffer) > 0) 
              no_ret(buffer);

	    if (strlen(buffer) == 0) {
	      weed_header = 0;		/* past header! */
	      weeding_out = 0;
	    }

	    lines--;

	    if (notesfile) {	/* treat notes differently! */

	      if (filter && (first_word(buffer, NOTES_HEADER) ||
	          first_word(buffer, NOTES_FOOTER)) ) 
	        /*** weed this line out of the display! ***/;
	      else if (show_line(buffer, &lines_on_screen, &lines)) {
	          if (cursor_control) transmit_functions(ON);
	          return(TRUE);
	      }
	    }

	    else { /* "normal" message */

	      if (weed_header && matches_weedlist(buffer)) 
	        weeding_out = 1;	 /* aha!  We don't want to see this! */
	      else if (buffer[0] == '[') {
	        if (strcmp(buffer, START_ENCODE)==0)
		  crypted++;
	        else if (strcmp(buffer, END_ENCODE)==0)
	          crypted--;
	        else if (crypted) {
                  encode(buffer);
	          if (show_line(buffer, &lines_on_screen, &lines)) {
	            if (cursor_control) transmit_functions(ON);
	            return(TRUE);
	          }
	        }
	        else
	          if (show_line(buffer, &lines_on_screen, &lines)) {
	            if (cursor_control) transmit_functions(ON);
	            return(TRUE);
	          }
	      }
	      else if (crypted) {
	        if (! gotten_key++) getkey(OFF);
	        encode(buffer);
	        if (show_line(buffer, &lines_on_screen, &lines)) {
	          if (cursor_control) transmit_functions(ON);
	          return(TRUE);
	        }
	      }
	      else if (weeding_out) {
	        weeding_out = (whitespace(buffer[0]));	/* 'n' line weed */
	        if (! weeding_out) 	/* just turned on! */
	          if (show_line(buffer, &lines_on_screen, &lines)) {
	            if (cursor_control) transmit_functions(ON);
	            return(TRUE);
	          }
	      } 
	      else
	        if (show_line(buffer, &lines_on_screen, &lines)) {
	          if (cursor_control) transmit_functions(ON);
	          return(TRUE);
	        }
	    }
	  }
	

	PutLine(LINES-1, 0, "Please press <space> to return: ");
	Raw(ON);
	(void) ReadCh();
        if (cursor_control) transmit_functions(ON);
	return(TRUE);
}

int
show_line(buffer, lines_on_screen, total) 	
char *buffer;
int  *lines_on_screen, *total;
{
	/** Displays the given line if it can.  if not, it will put the
	    'ole 'space to continue' prompt on the bottom of the screen
	    and wait for either a 'space' or 'return'.  If 'return' is
	    hit (or 'q'), then it will return non-zero, otherwise it'll
	    return zero.
	**/

	static char overlap [LONG_SLEN];
	       char mybuffer[SLEN], ch;
	       int  last_line_loc;

	dprint3("show_line(buffer=%s, on-screen=%d, total=%d)\n", 
		 buffer, *lines_on_screen, *total);

	last_line_loc     = *lines_on_screen;	/* one back... */

	*lines_on_screen += ((strlen(buffer) / COLUMNS) + 1);

	if (last_line_loc == 0 && title_messages) {
	  display_title(*total);
	  last_line_loc = 2;
	  *lines_on_screen += 2;
	}
		  
	  
	if (*lines_on_screen > LINES-2) {
	  if (*total > 0) {
	    sprintf(mybuffer, "%d line%s left", *total, plural(*total));
	    PutLine(LINES-1, COLUMNS-20, "%s", mybuffer);
	    PutLine(LINES-1, 0, "Press <space> to continue: ");
	  }
	  else
	    PutLine(LINES-1, 0, "Please press <space> to return: ");
	  Raw(ON);
	  ch = ReadCh();
	  if (ch == '\n' || ch == '\r' || tolower(ch) == 'q')
	    return(TRUE);
	  if (ch == ' ' && *total == 0)	/* don't want '0' lines left */
	    return(TRUE);
	  Raw(OFF);
	  ClearScreen();
	  *lines_on_screen = 0;

	  if (title_messages) {
	    display_title(*total);
	    *lines_on_screen = 2;
	  }
		  
	  PutLine(*lines_on_screen, 0, "%s", overlap);
	  *lines_on_screen  += ((strlen(overlap) / COLUMNS) + 1);
	  last_line_loc = *lines_on_screen;
	  *lines_on_screen += ((strlen(buffer) / COLUMNS) + 1);
	}

	PutLine(last_line_loc, 0, "%s", buffer);      

	if (*lines_on_screen > LINES-6)		/* in case next is too LONG */
	  strcpy(overlap, buffer);
	
	return(FALSE);
}

display_title(lines_into_message)
int lines_into_message;
{
	/** Display top title, including "Page N" **/
	
	register int page;

	dprint1("displaytitle(lines-into-message=%d)\n", lines_into_message);

	page = (int) (lines_into_message) / (LINES - 4);
	
	PutLine(0,0,top_of_screen_left);

	PutLine(0, COLUMNS-strlen(top_of_screen_right), 
	        top_of_screen_right, page);
}
END-OF-FILE

size=`wc -c < src/showmsg.c`

if [ $size != 7604 ]
then
  echo Warning: src/showmsg.c changed - should be 7604 bytes, not $size bytes
fi

chmod 644 src/showmsg.c

# ---------- file src/softkeys.c ----------


if [ -f src/softkeys.c ]
then
  echo File 'src/softkeys.c' already exists\!
  exit 1
fi

echo extracting file src/softkeys.c...
cat << 'END-OF-FILE' > src/softkeys.c
/**			softkeys.c			**/

/** This file and associated routines: (C) Copyright 1986, Dave Taylor **/

#include <stdio.h>
#include "headers.h"

define_softkeys(level)
int level;
{
	dprint0("define_softkeys(level=");

	if (! hp_softkeys) {
	  dprint0("0) [error: no softkeys!]\n");
	  return;
	}

	if (level == MAIN) {
	  dprint0("MAIN)\n");
	  if (notesfile) {
	    define_key(f1, "  Show    Note",   "\r");
	    define_key(f2, "  Reply  to Note",  "r");
	    define_key(f3, " Change  Mailbox", "c");
	    define_key(f4, "  Save    Note",   "s");
	    define_key(f5, " Delete/Undelete", "^");
	    define_key(f6, " Print    Note",   "p");
	    define_key(f7, "  HELP",           "?");
	    define_key(f8, "  Quit     Msg",   "q");
	  }
	  else {
	    define_key(f1, "  Show     Msg",   "\r");
	    define_key(f2, "  Mail     Msg",   "m");
	    define_key(f3, "  Reply  to Msg",  "r");
	    define_key(f4, " Change  Mailbox", "c");
	    define_key(f5, "  Save     Msg",   "s");
	    define_key(f6, " Delete/Undelete", "^");
	    define_key(f7, " Print     Msg",   "p");
	    define_key(f8, "  Quit     Msg",   "q");
	  }
	}
	else if (level == ALIAS) {
	  dprint0("ALIAS)\n");
	  define_key(f1, " Alias  Current",  "a");
	  define_key(f2, " Check   Alias",   "c");
	  define_key(f3, " Make    Alias",   "m");
	  clear_key(f4);
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, " Return  to Msg",  "r");
	}
	else if (level == YESNO) {
	  dprint0("YES-NO)\n");
	  define_key(f1, "  Yes",  "y");
	  clear_key(f2);
	  clear_key(f3);
	  clear_key(f4);
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, "   No",  "n");
	}
	else if (level == READ) {
	  dprint0("READ)\n");
	  define_key(f1, "  Next    Page  ", " ");
	  clear_key(f2);
	  clear_key(f3);
	  clear_key(f4);
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, " Return  to Msg", "\n");
	}
	else if (level == CHANGE) {
	  dprint0("CHANGE)\n");
	  define_key(f1, "  Mail  Directry", "=/");
	  define_key(f2, "  Home  Directry", "~/");
	  clear_key(f3);
	  define_key(f4, "Incoming Mailbox", "!\n");
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, " Cancel", "\n");
	}

	softkeys_on();
}

define_key(key, display, send)
int key;
char *display, *send;
{

	char buffer[30];

	dprint3("define-key(key=%d, display='%s', send='%s'\n",
	         key, display, send);

	sprintf(buffer,"%s%s", display, send);

	printf("%c&f%dk%dd%dL%s", ESCAPE, key,
		strlen(display), strlen(send), buffer);
}

softkeys_on()	
{ 
	/* turn on softkeys (esc&jB) and turn on MENU and USER/SYSTEM */

	if (hp_softkeys) 
	  printf("%c&jB%c&jR", ESCAPE, ESCAPE); 
}

softkeys_off()	
{ 
	/* turn off softkeys (esc&j@) */

	if (hp_softkeys) 
	  printf("%c&j@", ESCAPE); 
}

clear_key(key)  
{ 	
	/** set a key to nothing... **/

	if (hp_softkeys) 
	   define_key(key, "                ", ""); 
}
END-OF-FILE

size=`wc -c < src/softkeys.c`

if [ $size != 2900 ]
then
  echo Warning: src/softkeys.c changed - should be 2900 bytes, not $size bytes
fi

chmod 644 src/softkeys.c

echo done

exit 0