[mod.sources] v06i033: Elm mail system

sources-request@mirror.UUCP (06/30/86)

Submitted by: Dave Taylor <pyramid!hplabs!hpldat!taylor>
Mod.sources: Volume 6, Issue 33
Archive-name: elm/Part08

# Continuation of Shell Archive, created by hpldat!taylor

# This is part 8

# 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


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

# ---------- file src/utils.c ----------

filename="src/utils.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/utils.c...
fi

cat << 'END-OF-FILE' > $filename
/**		utils.c		**/

/** Utility routines for ELM 

    All routines herein: (C) Copyright 1985 Dave Taylor
**/

#include "headers.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

#ifdef BSD
#undef tolower
#endif

#include <signal.h>

emergency_exit()
{
	/** used in dramatic cases when we must leave without altering
	    ANYTHING about the system... **/

	dprint0(1,
     "\nERROR: Something dreadful is happening!  Taking emergency exit!!\n\n");
	dprint0(1,"  possibly leaving behind the following files;\n");
	dprint2(1,"     The mailbox tempfile : %s%s\n", temp_mbox, username);
	dprint2(1,"     The mailbox lock file: %s%s.lock\n", mailhome, username);
	dprint2(1,"     The composition file : %s%d\n", temp_file, getpid());
	dprint2(1,"     The header comp file : %s%d\n", temp_file, getpid()+1);
	dprint2(1,"     The readmsg data file: %s/%s\n", home, readmsg_file);

	Raw(OFF);
	if (cursor_control)  transmit_functions(OFF);
	if (hp_terminal)     softkeys_off();

	if (cursor_control)
	  MoveCursor(LINES, 0);

	PutLine0(LINES,0, "\nEmergency Exit taken!  All temp files intact!\n\n");

	exit(1);
}

leave(val)
int val;	/* not used, placeholder for signal catching! */
{
	char buffer[SLEN];

	dprint0(2,"\nLeaving mailer normally (leave)\n");

	Raw(OFF);
	if (cursor_control)  transmit_functions(OFF);
	if (hp_terminal)     softkeys_off();

	sprintf(buffer,"%s%d",temp_file, getpid());  /* editor buffer */
	(void) unlink(buffer);

	sprintf(buffer,"%s%d",temp_file, getpid()+1);  /* editor buffer */
	(void) unlink(buffer);

	sprintf(buffer,"%s%s",temp_mbox, username);  /* temp mailbox */
	(void) unlink(buffer);

	sprintf(buffer,"%s/%s", home, readmsg_file);  /* readmsg temp */
	(void) unlink(buffer);

	sprintf(buffer,"%s%s.lock",mailhome, username); /* lock file */
	(void) unlink(buffer);

	if (! mail_only) {
	  MoveCursor(LINES,0);
	  Writechar('\n');
	}

	exit(0);
}

leave_locked(val)
int val;	/* not used, placeholder for signal catching! */
{
	/** same as leave routine, but don't disturb lock file **/

	char buffer[SLEN];

        dprint0(3,
	    "\nLeaving mailer due to presence of lock file (leave_locked)\n");

	Raw(OFF);
	if (cursor_control)  transmit_functions(OFF);
	if (hp_terminal)     softkeys_off();

	sprintf(buffer,"%s%d",temp_file, getpid());  /* editor buffer */
	(void) unlink(buffer);

	sprintf(buffer,"%s%d",temp_file, getpid()+1);  /* editor buffer */
	(void) unlink(buffer);

	sprintf(buffer,"%s%s",temp_mbox, username);  /* temp mailbox */
	(void) unlink(buffer);

	MoveCursor(LINES,0);
	Writechar('\n');

	exit(0);
}

int
get_page(msg_pointer)
int msg_pointer;
{
	/** ensure that 'current' is on the displayed page,
	    returning non-zero iff the page changed! **/

	register int first_on_page, last_on_page;

	first_on_page = (header_page * headers_per_page) + 1;

	last_on_page = first_on_page + headers_per_page - 1;

	if (msg_pointer > last_on_page) {
	  header_page = (int) (msg_pointer-1) / headers_per_page;
	  return(1);
	}
	else if (msg_pointer < first_on_page) {
	  header_page = (int) (msg_pointer-1) / headers_per_page;
	  return(1);
	}
	else
	  return(0);
}

int
copy_to_self(buffer)
char *buffer;
{
	/** returns true iff buffer = 'Cc: username' where username
	    is the account name of the person sending the message.
	    Used for weeding out 'Cc:' lines from the messages if
	    'weed' is turned on. **/

	/** note: tail_of() is located in file "strings.c"  **/

	char name[SLEN], buf[SLEN];
	register int i=0, j=0;
	
	tail_of(header_table[current-1].from, name, FALSE);

	while (name[i] != '!' && i < strlen(name))
	   i++;

	if (name[i] == '!') {
	  for (i++; i < strlen(name); i++)
	    name[j++] = name[i];

	  name[j] = 0;
	}

	sprintf(buf, "Cc: %s\n", name);

	return( strcmp(buf, buffer) == 0 );
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 3786 ]
  then
    echo $filename changed - should be 3786 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/validname.c ----------

filename="src/validname.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/validname.c...
fi

cat << 'END-OF-FILE' > $filename
/**			validname.c			**/

/** This routine takes a single address, no machine hops or
    anything, and returns 1 if it's valid and 0 if not.  The
    algorithm it uses is the same one that uux uses, namely:

	1. Is there a file '/usr/mail/%s'?  
	2. Is there a password entry for %s?
	
   (C) Copyright 1986 Dave Taylor 
**/

#include <stdio.h>

#include "defs.h"

int
valid_name(name)
char *name;
{
	/** does what it says above, boss! **/

#ifdef NOCHECK_VALIDNAME

	return(1);		/* always say it's okay! */

#else
	char filebuf[SLEN];

	sprintf(filebuf,"%s/%s", mailhome, name);
	
	if (access(filebuf, ACCESS_EXISTS) == 0)
	  return(1);

	if (getpwnam(name) != NULL)
	  return(1);

	return(0);

#endif

}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 706 ]
  then
    echo $filename changed - should be 706 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/syscall.c ----------

filename="src/syscall.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/syscall.c...
fi

cat << 'END-OF-FILE' > $filename
/**			syscall.c		**/

/** These routines are used for user-level system calls, including the
    '!' command and the '|' commands...

    (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"

#include <signal.h>

char *argv_zero();	

int
subshell()
{
	/** spawn a subshell with either the specified command
	    returns non-zero if screen rewrite needed
	**/

	char command[SLEN];
	int  ret;

	PutLine0(LINES-3,COLUMNS-40,"(use the shell name for a shell)");
	PutLine0(LINES-2,0,"Shell Command: ");
	command[0] = '\0';
	(void) optionally_enter(command, LINES-2, 15, FALSE);
	if (strlen(command) == 0) {
	  MoveCursor(LINES-2,0);	CleartoEOLN();
	  return(0);
	}

	MoveCursor(LINES,0); 	CleartoEOLN();
	Raw(OFF);
	if (cursor_control)  transmit_functions(OFF);
	
	ret = system_call(command, USER_SHELL);

	PutLine0(LINES, 0, "\n\nPress <return> to return to ELM: ");

	Raw(ON);
	(void) getchar();
	if (cursor_control)  transmit_functions(ON);

	if (ret != 0) error1("Return code was %d", ret);
	return(1);
}

system_call(string, shell_type)
char *string;
int   shell_type;
{
	/** execute 'string', setting uid to userid... **/
	/** if shell-type is "SH" /bin/sh is used regardless of the 
	    users shell setting.  Otherwise, "USER_SHELL" is sent **/

	int status, pid, w;
	register int (*istat)(), (*qstat)();
	
	dprint2(2,"System Call: %s\n\t%s\n", shell_type == SH? "/bin/sh" : shell,
		string);

#ifdef NO_VM		/* machine without virtual memory! */
	if ((pid = fork()) == 0) {
#else
	if ((pid = vfork()) == 0) {
#endif
	  setuid(userid);	/* back to the normal user! */
	  setgid(groupid);	/* and group id		    */

	  if (strlen(shell) > 0 && shell_type == USER_SHELL) {
	    execl(shell, argv_zero(shell), "-c", string, 0);
	  }
	  else 
	    execl("/bin/sh", "sh", "-c", string, 0);
	  _exit(127);
	}

	istat = signal(SIGINT, SIG_IGN);
	qstat = signal(SIGQUIT, SIG_IGN);

	while ((w = wait(&status)) != pid && w != -1)
		;

	if (w == -1) status = -1;
	
	signal(SIGINT, istat);
	signal(SIGQUIT, qstat);

	return(status);
}

int
do_pipe()
{
	/** pipe the tagged messages to the specified sequence.. **/

	char command[SLEN], buffer[LONG_SLEN], message_list[SLEN];
	register int  ret, tagged = 0, oldstat, i;

	message_list[0] = '\0';	/* NULL string to start... */

	for (i=0; i < message_count; i++)
	  if (ison(header_table[i].status, TAGGED)) {
	    sprintf(message_list,"%s %d", message_list, 
		    header_table[i].index_number);
	    tagged++;
	  }

	if (tagged > 1)
	  PutLine0(LINES-2,0,"Pipe tagged msgs to: ");
	else if (tagged) 
	  PutLine0(LINES-2,0,"Pipe tagged msg to : ");
	else {
	  PutLine0(LINES-2,0,"Pipe current msg to: ");
	  sprintf(message_list,"%d", header_table[current-1].index_number);
	}

	command[0] = '\0';

	(void) optionally_enter(command, LINES-2, 21, FALSE);
	if (strlen(command) == 0) {
	  MoveCursor(LINES-2,0);	CleartoEOLN();
	  return(0);
	}

	MoveCursor(LINES,0); 	CleartoEOLN();
	Raw(OFF);

	if (cursor_control)  transmit_functions(OFF);
	
	sprintf(buffer, "%s -f %s -h %s | %s",
		readmsg,
		infile,
		message_list,
		command);
	
	ret = system_call(buffer, USER_SHELL);

	PutLine0(LINES, 0, "\n\nPress <return> to return to ELM: ");
	Raw(ON);
	(void) getchar();
	if (cursor_control)  transmit_functions(ON);

	if (ret != 0) error1("Return code was %d", ret);
	return(1);
}

printmsg()
{
	/** Print current message or tagged messages using 'printout' 
	    variable.  Error message iff printout not defined! **/

	char buffer[LONG_SLEN], filename[SLEN], printbuffer[LONG_SLEN];
	char message_list[SLEN];
	register int  retcode, tagged = 0, oldstat, i;

	if (strlen(printout) == 0) {
	  error("Don't know how to print - option \"printmail\" undefined!");
	  return;
	}
	
	message_list[0] = '\0';	/* reset to null... */

	for (i=0; i < message_count; i++) 
	  if (header_table[i].status & TAGGED) {
	    sprintf(message_list, "%s %d", message_list, 
		    header_table[i].index_number);
	    tagged++;
	  }

	if (! tagged)
	  sprintf(message_list," %d", header_table[current-1].index_number);

	sprintf(filename,"%s%d", temp_print, getpid());

	if (in_string(printout, "%s"))
	  sprintf(printbuffer, printout, filename);
	else
	  sprintf(printbuffer, "%s %s", printout, filename);

	sprintf(buffer,"(%s -p -f %s%s > %s; %s 2>&1) > /dev/null",
		readmsg, infile, message_list, 
		filename,
		printbuffer);
	
	dprint0(2,"Printing system call...\n");

  	Centerline(LINES, "queueing...");

	if ((retcode = system_call(buffer, SH)) == 0) {
	  sprintf(buffer, "Message%s queued up to print", plural(tagged));
	  Centerline(LINES, buffer);
	}
	else
	  error1("Printout failed with return code %d", retcode);

	unlink(filename);	/* remove da temp file! */
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4692 ]
  then
    echo $filename changed - should be 4692 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/aliaslib.c ----------

filename="src/aliaslib.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/aliaslib.c...
fi

cat << 'END-OF-FILE' > $filename
/**			aliaslib.c			**/

/** Library of functions dealing with the alias system...

    (C) Copyright 1986 Dave Taylor
 **/

#include "headers.h"

char *expand_group(), *get_alias_address(), *expand_system();
char *get_token(), *strpbrk();

char *get_alias_address(name, mailing, depth)
char *name;
int   mailing, depth;
{
	/** return the line from either datafile that corresponds 
	    to the specified name.  If 'mailing' specified, then
	    fully expand group names.  Depth specifies the nesting
	    depth - the routine should always initially be called
	    with this equal 0.  Returns NULL if not found   **/

	static char buffer[VERY_LONG_STRING];
	int    loc;

	if (strlen(name) == 0)
	  return( (char *) NULL);

	if (user_files) 
	  if ((loc = find(name, user_hash_table, MAX_UALIASES)) >= 0) {
	    lseek(user_data, user_hash_table[loc].byte, 0L);
	    get_line(user_data, buffer);
	    if (buffer[0] == '!' && mailing)
	      return(expand_group(buffer, depth));
	    else if (strpbrk(buffer,"!@:") != NULL)	/* has a hostname */
	      return(expand_system(buffer, TRUE));
	    else
	      return((char *) buffer);
	  }
	 
	if (system_files) 
	  if ((loc = find(name, system_hash_table, MAX_SALIASES)) >= 0) {
	    lseek(system_data, system_hash_table[loc].byte, 0L);
	    get_line(system_data, buffer);
	    if (buffer[0] == '!' && mailing)
	      return(expand_group(buffer, depth));
	    else if (strpbrk(buffer,"!@:") != NULL)	/* has a hostname */
	      return(expand_system(buffer, TRUE));
	    else
	      return((char *) buffer);
	  }
	
	return( (char *) NULL);
}

char *expand_system(buffer, show_errors)
char *buffer;
int   show_errors;
{
	/** This routine will check the first machine name in the given path 
	    (if any) and expand it out if it is an alias...if not, it will 
	    return what it was given.  If show_errors is false, it won't 
	    display errors encountered...
	**/

	dprint2(6, "expand_system(%s, show-errors=%s)\n", buffer,
		onoff(show_errors));
	findnode(buffer, show_errors);

	return( (char *) buffer);
}
	      
char *expand_group(members, depth)
char *members;
int  depth;
{
	/** Given a group of names separated by commas, this routine
	    will return a string that is the full addresses of each
	    member separated by spaces. Depth is an internal counter
	    that keeps track of the depth of nesting that the routine
	    is in...it's for the get_token routine!  **/

	static char buffer[VERY_LONG_STRING];
	char   buf[LONG_STRING], *word, *address, *bufptr;

	strcpy(buf, members); 			/* parameter safety! */
	if (depth == 0) buffer[0] = '\0';	/* nothing in yet!   */
	bufptr = (char *) buf;			/* grab the address  */
	depth++;				/* one deeper!       */

	while ((word = get_token(bufptr, "!, ", depth)) != NULL) {
	  if ((address = get_alias_address(word, 1, depth)) == NULL) {
	    if (! valid_name(word)) {
	      dprint2(3, "Encountered illegal address %s (%s)\n",
			word, "expand_group");
	      error1("%s is an illegal address!", word);
	      return( (char *) NULL);
	    }
	    else if (strcmp(buffer, word) != 0)
	      sprintf(buffer, "%s%s%s", buffer,
		    (strlen(buffer) > 0)? ", ":"", word);
	  }
	  else if (strcmp(buffer, address) != 0)
	    sprintf(buffer,"%s%s%s", buffer, 
		    (strlen(buffer) > 0)? ", ":"", address);

	  bufptr = NULL;
	}

	return( (char *) buffer);
}

int
find(word, table, size)
char *word;
struct alias_rec table[];
int size;
{
	/** find word and return loc, or -1 **/
	register int loc;
	
	if (strlen(word) > 20) {
	  dprint2(3, "Too long alias name entered [%s] (%s)\n", word, "find");
	  error1("Bad alias name: %s.  Too long.\n", word);
	  return(-1);
	}

	loc = hash_it(word, size);

	while (strcmp(word, table[loc].name) != 0) {
	  if (table[loc].name[0] == '\0')
	    return(-1);
	  loc = (loc + 1) % size; 
	}

	return(loc);
}

int
hash_it(string, table_size)
char *string;
int   table_size;
{
	/** compute the hash function of the string, returning
	    it (mod table_size) **/

	register int i, sum = 0;
	
	for (i=0; string[i] != '\0'; i++)
	  sum += (int) string[i];

	return(sum % table_size);
}

get_line(fd, buffer)
int fd;
char *buffer;
{
	/* read from file fd.  End read upon reading either 
	   EOF or '\n' character (this is where it differs 
	   from a straight 'read' command!) */

	register int i= 0;
	char     ch;

	while (read(fd, &ch, 1) > 0)
	  if (ch == '\n' || ch == '\r') {
	    buffer[i] = 0;
	    return;
	  }
	  else
	    buffer[i++] = ch;
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4476 ]
  then
    echo $filename changed - should be 4476 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/domains.c ----------

filename="src/domains.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/domains.c...
fi

cat << 'END-OF-FILE' > $filename
/**			domains.c			**/

/** This file contains all the code dealing with the expansion of 
    domain based addresses in Elm.  It uses the file "domains" as
    defined in the sysdefs.h file.

    (C) Copyright 1986 Dave Taylor

    From a file format and idea in "uumail" - designed by Stan Barber.
**/

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

#include "headers.h"

/** define the various characters that we can encounter after a "%" sign
    in the template file...
**/

#define USERNAME	'U'	/* %U = the name of the remote user */
#define HOSTNAME	'N'	/* %N = the remote machine name     */
#define FULLNAME	'D'	/* %D = %N + domain info given      */
#define NPATH		'R'	/* %R = path to %N from pathalias   */
#define PPATH		'P'	/* %P = path to 'P' from pathalias  */
#define OBSOLETE	'S'	/* %S = (used to be suffix string)  */

/** and finally some characters that are allowed in user/machine names **/

#define okay_others(c)	(c == '-' || c == '^' || c == '$' || c == '_')

/** and some allowed ONLY in the username field **/

#define special_chars(c)	(c == '%' || c == ':')

char *find_path_to(), *expand_domain(), *match_and_expand_domain();
	      
open_domain_file()
{
	if ((domainfd = fopen(domains, "r")) == NULL) {
	  dprint1(1, "Can't open file %s as domains file (open_domain_file)\n", 
		  domains);
	}
	else {
	  dprint1(2,
            "\nOpened '%s' as the domain database. (open_domain_file)\n\n", 
		  domains);
	}
	
	/* if it fails it'll instantiate domainfd to NULL which is
	   exactly what we want to have happen!! */
}

char *expand_domain(buffer)
char *buffer;
{
	/** Expand the address 'buffer' based on the domain information, 
	    if any.  Returns NULL if it can't expand it for any reason.
	**/

	char name[2*NLEN], address[2*NLEN], domain[2*NLEN];
	char *match_and_expand_domain();

	if (domainfd == NULL) return(NULL);	/* no file present! */

	if (explode(buffer, name, address, domain)) 
	  return( match_and_expand_domain(domain, name, address) );
	else {	/* invalid format - not "user@host.domain" */
	  dprint1(3, 
		 "Invalid format for domain expansion: %s (expand_domain)\n", 
		   buffer);
	  return(NULL);
	}
}	

int
explode(buffer, name, address, domain)
char *buffer, *name, *address, *domain;
{
	/** Break buffer, if in format name@machine.domain, into the
	    component parts, otherwise return ZERO and don't worry
	    about the values of the parameters!
	**/
	
	register int i, j = 0;

	/** First get the name... **/

	for (i=0; buffer[i] != '@'; i++) {
	  if (! isalnum(buffer[i]) && ! okay_others(buffer[i]) && ! 
		special_chars(buffer[i]))
	    return(0);			/* invalid character in string! */
	  name[i] = buffer[i];
	}

	name[i++] = '\0';

	/** now let's get the machinename **/

	while (buffer[i] != '.') {
	  if (! isalnum(buffer[i]) && ! okay_others(buffer[i]))
	     return(0);			/* invalid character in string! */
	  address[j++] = buffer[i++];
	}
	address[j] = '\0';

	j = 0;

	/** finally let's get the domain information (there better be some!) **/

	while (buffer[i] != '\0') {
	  if (! isalnum(buffer[i]) && ! okay_others(buffer[i]) && 
	        buffer[i] != '.')
	    return(0);		      /* an you fail again, bozo! */
	  domain[j++] = toupper(buffer[i]);
	  i++;
	}

	domain[j] = '\0';
	
	return(j);		/* if j == 0 there's no domain info! */
}
        
char *match_and_expand_domain(domain, name, machine)
char *domain, *name, *machine;
{
	/** Given the domain, try to find it in the domain file and
   	    if found expand the entry and return the result as a 
	    character string...
	**/

	static char address[SLEN];
	char   buffer[SLEN], domainbuff[NLEN];
	char   field1[2*NLEN], field2[2*NLEN], field3[2*NLEN];
	char   *path, *template, *expanded, *mydomain;
	int    matched = 0, in_percent = 0;
	register int i, j = 0;

	address[j] = '\0';

	domainbuff[0] = '\0';
	mydomain = (char *) domainbuff;		    /* set up buffer etc */

	do { 
	  rewind(domainfd);		           /* back to ground zero! */

	  if (strlen(mydomain) > 0) {		   /* already in a domain! */
	    mydomain++;		 		      /* skip leading '.' */
	    while (*mydomain != '.' && *mydomain != ',') 
	      mydomain++;	 		      /* next character   */
	    if (*mydomain == ',')
	      return (NULL);  			  /* didn't find domain!  */
	  }
	  else
	    sprintf(mydomain, "%s,", domain);		/* match ENTIRELY! */

	/* whip through file looking for the entry, please... */

	while (fgets(buffer, SLEN, domainfd) != NULL) {
	  if (buffer[0] == '#')				  /* skip comments */
	    continue;
	  if (strncmp(buffer, mydomain, strlen(mydomain)) == 0) { /* match? */
	     matched++;	/* Gotcha!  Remember this momentous event! */
	     break;
	  }
	}

	if (! matched) 
	   continue;		/* Nothing.  Not a sausage!  Step through! */

	/** We've matched the domain! **/

	no_ret(buffer);

	(void) strtok(buffer, ",");	/* skip the domain info */

	strcpy(field1, strtok(NULL, ","));	/* fun 		*/
	strcpy(field2, strtok(NULL, ","));	/*    stuff     */
	strcpy(field3, strtok(NULL, ","));	/*       eh?    */
	
	path = (char *) NULL;

	/* now we merely need to figure out what permutation this is! */

	if (field3 == NULL || strlen(field3) == 0)
	  if (field2 == NULL || strlen(field2) == 0)
	    template = (char *) field1;
	  else {
	    path     = (char *) field1;
	    template = (char *) field2;
	  }
	else {
	  dprint1(2,"Domain info for %s from file broken into THREE fields!!\n",
		   domain);
	  dprint3(2, "-> %s\n-> %s\n-> %s\n", field1, field2, field3);
	  error1("Warning: domain %s uses a defunct field!!", domain);
	  sleep(2);
	  path     = (char *) field1;
	  template = (char *) field3;
	}

	if (strlen(path) > 0 && path[0] == '>') 
	   path++;	/* skip the '>' character, okay? */

	j = 0;		 	/* address is zero, right now, right?? */
	address[j] = '\0';	      /* make sure string is too! */

	for (i=0; i < strlen(template); i++) {
	  if (template[i] == '%') {
	    if (! in_percent)			   /* just hit a NEW percent! */
	      in_percent = 1;
	    else {		  /* just another percent sign on the wall... */
	      address[j++] = '%';
	      address[j] = '\0';		     /* ALWAYS NULL terminate */
	      in_percent = 0;
	    }
	  }
	  else if (in_percent) {	       /* Hey! a real command string */
	    in_percent = 0;
	    switch (template[i]) {
	      case USERNAME: strcat(address, name);		break;
	      case HOSTNAME: strcat(address, machine);		break;
	      case FULLNAME: strcat(address, machine);
			     strcat(address, domain);		break;
	      case NPATH   : 

		 if ((expanded = find_path_to(machine, FALSE)) == NULL) {
		    dprint2(3,"\nCouldn't expand system path '%s' (%s)\n\n",
			    machine, "domains");
	            error1("Couldn't find a path to %s!", machine);
	            sleep(2);
	            return(NULL);	/* failed!! */
	         }
	         strcat(address, expanded);	/* isn't this fun??? */

	         break;

	      case PPATH   : 

		 if ((expanded = find_path_to(path, FALSE)) == NULL) {
		    dprint2(3,"\nCouldn't expand system path '%s' (%s)\n\n",
			    path, "domains");
	            error1("I Couldn't find a path to %s!", path);
	            sleep(2);
	            return(NULL);	/* failed!! */
	         }
	         strcat(address, expanded);	/* isn't this fun??? */

	         break;

	      case OBSOLETE:	/* fall through.. */
	      default      : dprint2(1,
	     "\nError: Bad sequence in template file for domain '%s': %%%c\n\n",
			domain, template[i]);
	    }
	    j = strlen(address);
	  }
	  else {
	    address[j++] = template[i];
	    address[j] = '\0';			/* null terminate */
	  }
	}	

	address[j] = '\0';

	} while (strlen(address) < 1);

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

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 7635 ]
  then
    echo $filename changed - should be 7635 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/fileio.c ----------

filename="src/fileio.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/fileio.c...
fi

cat << 'END-OF-FILE' > $filename
/**			fileio.c			**/

/** File I/O routines, including deletion from the mailbox! 

    (C) Copyright 1986 Dave Taylor
**/

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

#ifdef BSD
#undef tolower
#endif

copy_message(prefix, dest_file, remove_header, remote)
char *prefix;
FILE *dest_file;
int  remove_header, remote;
{
	/** Copy current message to destination file, with optional 'prefix' 
	    as the prefix for each line.  If remove_header is true, it will 
	    skip lines in the message until it finds the end of header line...
            then it will start copying into the file... If remote is true
	    then it will append "remote from <hostname>" at the end of the
	    very first line of the file (for remailing) **/

    char buffer[LONG_SLEN];
    register int ok = 1, lines, in_header = 1, first_line = TRUE;

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

    if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
       dprint2(1,"ERROR: Attempt to seek %d bytes into file failed (%s)",
		header_table[current-1].offset, "copy_message");
       error1("ELM [seek] failed trying to read %d bytes into file",
	     header_table[current-1].offset);
       return;
    }

    /* how many lines in message? */

    lines = header_table[current-1].lines;

    /* now while not EOF & still in message... copy it! */

    while (ok && lines--) {
      ok = (int) (fgets(buffer, LONG_SLEN, mailfile) != NULL);
      if (strlen(buffer) < 2) in_header = 0;
      if (ok) 
	if (! (remove_header && in_header))
	  if (first_line && remote) {
	    no_ret(buffer);
	    fprintf(dest_file, "%s%s remote from %s\n",
		    prefix, buffer, hostname);
	    first_line = FALSE;
	  }
	  else if (! in_header && first_word(buffer, "From ")) {
	    dprint0(1,"\n*** Internal Problem...Tried to add the following;\n");
	    dprint1(1,"  '%s'\nto output file (copy_message) ***\n", buffer);
	  }
	  else
	    fprintf(dest_file, "%s%s", prefix, buffer);
    }
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 1967 ]
  then
    echo $filename changed - should be 1967 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

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

filename="src/return_addr.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/return_addr.c...
fi

cat << 'END-OF-FILE' > $filename
/**			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).

    Added: the ability to respond to messages that were originally
    sent by the user (That is, the "savemail" file format messages)
    by reading the return address, seeing the "To:" prefix and then
    calling the "get_existing_return()" routine.  Currently this does
    NOT include any "Cc" lines in the message, just the "To:" line(s).

    Also added the PREFER_UUCP stuff for listing reasonable addresses
    and such...*sigh*

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

#include "headers.h"

#include <errno.h>

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

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

extern int errno;

char *error_name();

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...
	**/

#ifdef PREFER_UUCP

	/** first off, let's see if we need to strip off the localhost
	    address crap... **/

	/** if we have a uucp part (e.g.a!b) AND the bogus address...**/

	if (chloc(address,'!') != -1 && in_string(address, BOGUS_INTERNET))
	  address[strlen(address)-strlen(BOGUS_INTERNET)] = '\0';
#endif

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

	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;

	/** 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';

	  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;

	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);

	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;

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

	reverse(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 '!' */
	      sprintf(address, "%s@%s", name, buffer);
	    }
	    else 
	      sprintf(address, "%s%s", buffer, name);
	    return;		/* success! */
	  }
	  passes++;
	}

	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;

    /** 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, TRUE));
      return;
    }

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

    if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
	dprint3(1,"Error: seek %ld bytes into file hit errno %s (%s)", 
		header_table[current-1].offset, error_name(errno), 
	        "get_return");
	error2("couldn't seek %d bytes into file (%s)",
	       header_table[current-1].offset, error_name(errno));
	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! */

    if (first_word(buffer, "To:")) 		/* response to savecopy!  */
       get_existing_address(buffer);
}

get_existing_address(buffer)
char *buffer;
{
	/** This routine is called when the message being responded to has
	    "To:xyz" as the return address, signifying that this message is
	    an automatically saved copy of a message previously sent.  The
	    correct to address can be obtained fairly simply by reading the
	    To: header from the message itself and (blindly) copying it to
	    the given buffer.  Note that this header can be either a normal
	    "To:" line (Elm) or "Originally-To:" (previous versions e.g.Msg)
	**/

	char mybuf[LONG_STRING];
	register char ok = 1, in_to = 0;

	buffer[0] = '\0';

	/** first off, let's get to the beginning of the message... **/

        if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
	    dprint3(1,"Error: seek %ld bytes into file hit errno %s (%s)", 
		    header_table[current-1].offset, error_name(errno), 
		    "get_existing_address");
	    error2("couldn't seek %d bytes into the file (%s)",
	           header_table[current-1].offset, error_name(errno));
	    return;
        }
 
        /** okay!  Now we're there!  **/

        while (ok) {
          ok = (int) (fgets(mybuf, LONG_STRING, mailfile) != NULL);
	  no_ret(mybuf);	/* remove return character */

          if (first_word(mybuf, "To: ")) {
	    in_to = TRUE;
	    strcpy(buffer, (char *) mybuf + strlen("To: "));
          }
	  else if (first_word(mybuf, "Original-To:")) {
	    in_to = TRUE;
	    strcpy(buffer, (char *) mybuf + strlen("Original-To:"));
	  }
	  else if (in_to && whitespace(mybuf[0])) {
	    strcat(buffer, " ");		/* tag a space in   */
	    strcat(buffer, (char *) mybuf + 1);	/* skip 1 whitespace */
	  }
	  else if (strlen(mybuf) < 2)
	    return;				/* we're done for!  */
	  else
	    in_to = 0;
      }
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 9321 ]
  then
    echo $filename changed - should be 9321 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/addr_utils.c ----------

filename="src/addr_utils.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/addr_utils.c...
fi

cat << 'END-OF-FILE' > $filename
/**			addr_utils.c			**/

/** This file contains addressing utilities 

    (C) Copyright 1986 Dave Taylor 
**/

#include "headers.h"

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

#ifdef BSD
#undef tolower
#endif

char *shift_lower(), *get_alias_address(), *get_token(), *strtok();

int
talk_to(sitename)
char *sitename;
{
	/** If we talk to the specified site, return true, else
	    we're going to have to expand this baby out, so 
	    return false! **/

	struct lsys_rec  *sysname;

	sysname = talk_to_sys;

	if (sysname == NULL) {
	 dprint0(2,
		"Warning - talk_to_sys is currently set to NULL! (talk_to)\n");
	 return(0);
	}

	while (sysname != NULL) {
	  if (strcmp(sysname->name, sitename) == 0)
	    return(1);
	  else
	    sysname = sysname->next;
	}

	return(0);
}

remove_domains(host)
char *host;
{
	/** Remove all entries following the first '.' to ensure that
	    entries like "MIT.ARPA" will match "MIT" in the database
	**/

	register int loc = 0;

	while (host[loc] != '.' && host[loc] != '\0')
	  loc++;

	if (host[loc] == '.') host[loc] = '\0';
}

add_site(buffer, site, lastsite)
char *buffer, *site, *lastsite;
{
	/** add site to buffer, unless site is 'uucp', current machine, or
            site is the same as lastsite.   If not, set lastsite to
            site.
	**/

	char local_buffer[LONG_SLEN];

	if (strcmp(site, "uucp") != 0)
	  if (strcmp(site, lastsite) != 0) 
	    if (strcmp(site, hostname) != 0) {
	      if (buffer[0] == '\0')
	        strcpy(buffer, strip_parens(site));         /* first in list! */
	      else {
	        sprintf(local_buffer,"%s!%s", buffer, strip_parens(site));
	        strcpy(buffer, local_buffer);
	      }
	      strcpy(lastsite, strip_parens(site)); /* don't want THIS twice! */
	    }
}

#ifdef USE_EMBEDDED_ADDRESSES

get_address_from(prefix, line, buffer)
char *prefix, *line, *buffer;
{
	/** This routine extracts the address from either a 'From:' line
	    or a 'Reply-To:' line...the algorithm is quite simple, too:
	    increment 'line' past header, then check last character of 
	    line.  If it's a '>' then the address is contained within '<>'
	    and if it's a ')' then the address is in the 'clear'... **/

	register int i, j = 0;
	
	no_ret(line);

	line = (char *) (line + strlen(prefix) + 1);

	if (line[strlen(line)-1] == '>') {
	  for (i=strlen(line)-2; i > -1 && line[i] != '<'; i--)
	    buffer[j++] = line[i];
	  buffer[j] = 0;
	  reverse(buffer);
	}
	else {	/* either ')' or address in the clear... */
	  for (i=0; i < strlen(line) && line[i] != '('; i++)
	    buffer[j++] = line[i];
	  if (buffer[j-1] == '(') j--;
	  buffer[j] = 0;
	}
}

#endif

translate_return(addr, ret_addr)
char *addr, *ret_addr;
{
	/** Return ret_addr to be the same as addr, but with the login 
            of the person sending the message replaced by '%s' for 
            future processing... 
	    Fixed to make "%xx" "%%xx" (dumb 'C' system!) 
	**/

	register int loc, loc2, index = 0;
	
	loc2 = chloc(addr,'@');
	if ((loc = chloc(addr, '%')) < loc2)
	  loc2 = loc;

	if (loc2 != -1) {	/* ARPA address. */
	  /* algorithm is to get to '@' sign and move backwards until
	     we've hit the beginning of the word or another metachar.
	  */
	  for (loc = loc2 - 1; loc > -1 && addr[loc] != '!'; loc--)
	     ;
	}
	else {			/* usenet address */
	  /* simple algorithm - find last '!' */

	  loc2 = strlen(addr);	/* need it anyway! */

	  for (loc = loc2; loc > -1 && addr[loc] != '!'; loc--)
	      ;
	}
	
	/** now copy up to 'loc' into destination... **/

	while (index <= loc) {
	  ret_addr[index] = addr[index];
	  index++;
	}

	/** now append the '%s'... **/

	ret_addr[index++] = '%';
	ret_addr[index++] = 's';

	/** and, finally, if anything left, add that **/

	while (loc2 < strlen(addr)) {
	  ret_addr[index++] = addr[loc2++];
	  if (addr[loc2-1] == '%')	/* tweak for "printf" */
	    ret_addr[index++] = '%';
	}
	
	ret_addr[index] = '\0';
}

build_address(to, full_to)
char *to, *full_to;
{
	/** loop on all words in 'to' line...append to full_to as
	    we go along, until done or length > len **/

	register int i, changed = 0;
	char word[SLEN], *ptr, buffer[SLEN];
	char new_to_list[LONG_SLEN];

	new_to_list[0] = '\0';

	i = get_word(to, 0, word);

	full_to[0] = '\0';

	while (i > 0) {

	  if (strpbrk(word,"!@:") != NULL)
	    sprintf(full_to, "%s%s%s", full_to,
                    full_to[0] != '\0'? ", " : "", expand_system(word, 1));
	  else if ((ptr = get_alias_address(word, 1, 0)) != NULL)
	    sprintf(full_to, "%s%s%s", full_to, 
                    full_to[0] != '\0'? ", " : "", ptr);
	  else if (strlen(word) > 0) {
	    if (valid_name(word)) 
	      sprintf(full_to, "%s%s%s", full_to,
                      full_to[0] != '\0'? ", " : "", word);
	    else if (check_only) {
	      printf("(alias \"%s\" is unknown)\n\r",
		     word);
	      changed++;
	    }
	    else if (! isatty(fileno(stdin)) ) {	/* batch mode error! */
	      fprintf(stderr,"Cannot expand alias '%s'!\n\r", word);
	      fprintf(stderr,"Use \"checkalias\" to find valid addresses!\n\r");
	      dprint1(1,
		      "Can't expand alias %s - bailing out! (build_address)\n", 
		      word);
	      emergency_exit();
	    }
	    else {
	      dprint1(2,"Entered unknown address %s (build_address)\n", word);
	      sprintf(buffer, "'%s' is an unknown address.  Replace with: ", 
	              word);
	      word[0] = '\0';
	      if (! mail_only) 
	        PutLine0(LINES, 0, buffer);
		
	      (void) optionally_enter(word, LINES, strlen(buffer), FALSE);
	      if (strlen(word) > 0) {
	        sprintf(new_to_list, "%s%s%s", new_to_list,
			strlen(new_to_list) > 0? " ":"", word);
	        dprint1(3,"Replaced with %s (build_address)\n", word);
	      }
	      else
		dprint0(3,"Address removed from to list (build_address)\n");
	      if (mail_only) printf("\n\r");
	      changed++;
	      clear_error();
	      continue;
	    }
	  }

	  i = get_word(to, i, word);
	}

	if (changed)
	  strcpy(to, new_to_list);
}

int
real_from(buffer, entry)
char *buffer;
struct header_rec *entry;
{
	/***** Returns true iff 's' has the seven 'from' fields, (or
	       8 - some machines include the TIME ZONE!!!)
	       Initializing the date and from entries in the record 
	       and also the message received date/time.  *****/

	char junk[STRING], timebuff[STRING], holding_from[SLEN];
	int  eight_fields = 0;

	entry->year[0] = '\0';
	junk[0] = '\0';

	/* From <user> <day> <month> <day> <hr:min:sec> <year> */

	sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %s", timebuff, junk);

	if (timebuff[1] != ':' && timebuff[2] != ':') { 
	  dprint1(3,"real_from returns FAIL [bad time field] on\n-> %s\n", 
		  buffer);
	  return(FALSE);
	}
	if (junk[0] != '\0') {	/* try for 8 field entry */
	  junk[0] = '\0';
	  sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %*s %s", timebuff, junk);
	  if (junk[0] != '\0') {
	    dprint1(3,"real_from returns FAIL [too many fields] on\n-> %s\n", 
		    buffer);
	    return(FALSE);
	  }
	  eight_fields++;
	}

	/** now get the info out of the record! **/

	if (eight_fields) 
	  sscanf(buffer, "%s %s %s %s %s %s %*s %s",
	            junk, holding_from, entry->dayname, entry->month, 
                    entry->day, entry->time, entry->year);
	else
	  sscanf(buffer, "%s %s %s %s %s %s %s",
	            junk, holding_from, entry->dayname, entry->month, 
                    entry->day, entry->time, entry->year);
	
	strncpy(entry->from, holding_from, STRING);
	resolve_received(entry);
	return(entry->year[0] != '\0');
}

forwarded(buffer, entry)
char *buffer;
struct header_rec *entry;
{
	/** Change 'from' and date fields to reflect the ORIGINATOR of 
	    the message by iteratively parsing the >From fields... 
	    Modified to deal with headers that include the time zone
	    of the originating machine... **/

	char machine[SLEN], buff[SLEN], holding_from[SLEN];

	machine[0] = '\0';

	sscanf(buffer, "%*s %s %s %s %s %s %s %*s %*s %s",
	            holding_from, entry->dayname, entry->month, 
                    entry->day, entry->time, entry->year, machine);

	if (isdigit(entry->month[0])) { /* try for veeger address */
	  sscanf(buffer, "%*s %s %s%*c %s %s %s %s %*s %*s %s",
	            holding_from, entry->dayname, entry->day, entry->month, 
                    entry->year, entry->time, machine);
	}
	if (isalpha(entry->year[0])) { /* try for address including tz */
	  sscanf(buffer, "%*s %s %s %s %s %s %*s %s %*s %*s %s",
	            holding_from, entry->dayname, entry->month, 
                    entry->day, entry->time, entry->year, machine);
	}

	if (machine[0] == '\0')
	  sprintf(buff,"anonymous");
	else
	  sprintf(buff,"%s!%s", machine, holding_from);

	strncpy(entry->from, buff, STRING);
}

parse_arpa_from(buffer, newfrom)
char *buffer, *newfrom;
{
	/** try to parse the 'From:' line given... It can be in one of
	    two formats:
		From: Dave Taylor <hplabs!dat>
	    or  From: hplabs!dat (Dave Taylor)

	    Change 'newfrom' ONLY if sucessfully parsed this entry and
	    the resulting name is non-null! 
	    Added: removes quotes if name is quoted (12/12)
	    Added: only copies STRING characters...
	**/

	char temp_buffer[SLEN], *temp;
	register int i, j = 0;

	temp = (char *) temp_buffer;
	temp[0] = '\0';

	no_ret(buffer);		/* blow away '\n' char! */

	if (lastch(buffer) == '>') {
	  for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' &&
	       buffer[i] != '('; i++)
	    temp[j++] = buffer[i];
	  temp[j] = '\0';
	}
	else if (lastch(buffer) == ')') {
	  for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '(' &&
	       buffer[i] != '<'; i--)
	    temp[j++] = buffer[i];
	  temp[j] = '\0';
	  reverse(temp);
	}
	  
	if (strlen(temp) > 0) {		/* mess with buffer... */

	  /* remove leading spaces and quotes... */

	  while (whitespace(temp[0]) || quote(temp[0]))
	    temp = (char *) (temp + 1);		/* increment address! */

	  /* remove trailing spaces and quotes... */

	  i = strlen(temp) - 1;

	  while (whitespace(temp[i]) || quote(temp[i]))
	   temp[i--] = '\0';

	  /* if anything is left, let's change 'from' value! */

	  if (strlen(temp) > 0)
	    strncpy(newfrom, temp, STRING);
	}
}

parse_arpa_date(string, entry)
char *string;
struct header_rec *entry;
{
	/** Parse and figure out the given date format... return
	    the entry fields changed iff it turns out we have a
	    valid parse of the date!  **/

	char word[15][NLEN], buffer[SLEN], *bufptr;
	char *aword; 
	int  words = 0;

	strcpy(buffer, string);
	bufptr = (char *) buffer;

	/** break the line down into words... **/

	while ((aword = strtok(bufptr," \t '\"-/(),.")) != NULL) {
	  strcpy(word[words++], aword);
	  bufptr = NULL;
	}

	if (words < 6) {	/* strange format.  We're outta here! */
	  dprint1(3,"parse_arpa_date failed [less than six fields] on\n-> %s\n",
		  string);
	  return;
	}

	/* There are now five possible combinations that we could have:
	 
	    Date: day_number month_name year_number time timezone
	    Date: day_name day_number month_name year_number ...
	    Date: day_name month_name day_number time year_number
	    Date: day_name month_name day_number year_number time
	    Date: day_number month_name year_number time timezone day_name

	   Note that they are distinguishable by checking the first
	   character of the second, third and fourth words... 
	*/

	if (isdigit(word[1][0])) {			/*** type one! ***/
	  if (! valid_date(word[1], word[2], word[3])) {
	    dprint4(3,"parse_arpa_date failed [bad date: %s/%s/%s] on\n-> %s\n",
		  word[1], word[2], word[3], string);
	    return;		/* strange date! */
	  }
	  strncpy(entry->day, word[1], 3);
	  strncpy(entry->month, word[2], 3);
	  strncpy(entry->year,  word[3], 4);
	  strncpy(entry->time,  word[4], 10);
	}
	else if (isdigit(word[2][0])) {		        /*** type two! ***/
	  if (! valid_date(word[2], word[3], word[4])) {
	    dprint4(3,"parse_arpa_date failed [bad date: %s/%s/%s] on\n-> %s\n",
		  word[2], word[3], word[4], string);
	    return;		/* strange date! */
	  }
	  strncpy(entry->day, word[2], 3);
	  strncpy(entry->month, word[3], 3);
	  strncpy(entry->year,  word[4], 4);
	  strncpy(entry->time,  word[5], 10);
	}
	else if (isdigit(word[3][0])) {		
	  if (word[4][1] == ':' || 
              word[4][2] == ':') {	               /*** type three! ***/
	    if (! valid_date(word[3], word[2], word[5])) {
	     dprint4(3,
		"parse_arpa_date failed [bad date: %s/%s/%s] on\n-> %s\n",
		    word[3], word[2], word[5], string);
	      return;		/* strange date! */
	    }
	    strncpy(entry->year,  word[5], 4);
	    strncpy(entry->time,  word[4], 10);
	  }
	  else {				       /*** type four!  ***/ 
	    if (! valid_date(word[3], word[2], word[4])) {
	     dprint4(3,"parse_arpa_date failed [bad date: %s/%s/%s] on\n-> %s\n",
		    word[3], word[2], word[4], string);
	      return;		/* strange date! */
	    }
	    strncpy(entry->year,  word[4], 4);
	    strncpy(entry->time, word[5], 10);
	  }
	  strncpy(entry->day, word[3], 3);
	  strncpy(entry->month, word[2], 3);
	}
}

fix_arpa_address(address)
char *address;
{
	/** Given a pure ARPA address, try to make it reasonable.

	    This means that if you have something of the form a@b@b make 
            it a@b.  If you have something like a%b%c%b@x make it a%b@x...
	**/

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

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

	while ((host = get_token(addrptr, "%@", 2)) != 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) {
	       dprint0(2,
              "Can't build return address - hit MAX_HOPS (fix_arpa_address)\n");
	       error("Can't build return address - hit MAX_HOPS limit!");
	       return(1);
	    }
	  }
	  else 
	    host_count = i + 1;
	  addrptr = NULL;
	}

	/** rebuild the address.. **/

	address[0] = '\0';

	for (i = 0; i < host_count; i++)
	  sprintf(address, "%s%s%s", address, 
	          address[0] == '\0'? "" : 
	 	    (i == host_count - 1 ? "@" : "%"),
	          hosts[i]);

	return(0);
}

figure_out_addressee(buffer, mail_to)
char *buffer;
char *mail_to;
{
	/** This routine steps through all the addresses in the "To:"
	    list, initially setting it to the first entry (if mail_to
	    is NULL) or, if the user is found (eg "alternatives") to
	    the current "username".

	    Modified to know how to read quoted names...
	**/

	char *address, *bufptr, *strchr();
	register int index = 0, index2 = 0;
	
	if (equal(mail_to, username)) return;	/* can't be better! */

	bufptr = (char *) buffer;	/* use the string directly   */

	if (strchr(buffer,'"') != NULL) {	/* we have a quoted string */
	  while (buffer[index] != '"')
	    index++;
	  index++;	/* skip the leading quote */
	  while (buffer[index] != '"' && index < strlen(buffer))
	    mail_to[index2++] = buffer[index++];
	  mail_to[index2] = '\0';
	}
	else while ((address = strtok(bufptr, " ,\t\n\r")) != NULL) {
	  if (! okay_address(address, "don't match me!")) {
	    strcpy(mail_to, username);	/* it's to YOU! */
	    return;
	  }
	  else if (strlen(mail_to) == 0)	/* it's SOMEthing! */
	    get_return_name(address, mail_to, FALSE);

	  bufptr = (char *) NULL;	/* set to null */
	}

	return;
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 15408 ]
  then
    echo $filename changed - should be 15408 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/input_utils.c ----------

filename="src/input_utils.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/input_utils.c...
fi

cat << 'END-OF-FILE' > $filename
/**			input_utils.c			**/

/** Mindless I/O routines for ELM 
	
    (C) Copyright 1985 Dave Taylor
**/

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

extern int errno;		/* system error number */

int
want_to(question, dflt, echo_answer)
char *question, dflt;
int  echo_answer;
{
	/** Ask 'question' at LINES-2, COLUMNS-40, returning the answer in 
	    lower case.  If 'echo_answer', then echo answer.  'dflt' is the 
	    default answer if <return> is pressed. (Note: 'dflt' is also what 
	    will be returned if <return> is pressed!)
	**/
	register char ch, cols;

	cols = (strlen(question) < 30)? COLUMNS-40 : COLUMNS-50;

	PutLine3(LINES-3, cols,"%s%c%c", question, dflt, BACKSPACE);
	fflush(stdout);
	fflush(stdin);
	ch = tolower(ReadCh());

	if (echo_answer && ch > (char) ' ') {
	  Writechar(ch);
	  fflush(stdout);
	}

	return(ch == '\n' || ch == '\r' ? dflt : ch);
}

int
read_number(ch, max)
char ch;
int max;
{
	/** read a number, where 'ch' is the leading digit! 
	    If max < 10 then just return 'ch' **/
	
	char buff[SHORT_SLEN];
	int  num;

	if (max < 10)
	  return( (int) ch - '0' );
	
	buff[0] = ch;
	buff[1] = '\0';

	PutLine0(LINES-3, COLUMNS-40,"Set current message to :");
	if (optionally_enter(buff, LINES-3, COLUMNS-15, TRUE) == -1)
	  return(current);

	sscanf(buff,"%d", &num);
	return(num);
}

int
optionally_enter(string, x, y, append_current)
char *string;
int  x,y, append_current;
{
	/** Display the string on the screen and if RETURN is pressed, return 
	    it.  Otherwise, allow standard text input, including backspaces 
	    and such until RETURN is hit.  
	    If "append_current" is set, then leave the default string in 
	    place and edit AFTER it...assume 'x,y' is placing us at the
	    beginning of the string...
	    This routine returns zero unless INTERRUPT hit, then it returns
	    -1 and must be treated accordingly.
	**/

	char ch;
	register index = 0;

	PutLine1(x,y, "%s", string);	
	CleartoEOLN();
	if (! append_current)
	  MoveCursor(x,y);

	if (cursor_control)
	  transmit_functions(OFF);

	ch = getchar();

	if (ch == '\n' || ch == '\r') {
	  if (cursor_control)
	    transmit_functions(ON);
	  return(0);	/* we're done.  No change needed */
	}
	
	CleartoEOLN();

	index = (append_current? strlen(string) : 0);

	if (ch == kill_line) {
	  if (! mail_only)
	    MoveCursor(x,y);
          CleartoEOLN();
	  index = 0;
	}
	else if (ch != backspace) {
	  Writechar(ch);
	  string[index++] = ch;
	}
	else if (index > 0) {
	  index--;
	  Writechar(BACKSPACE);
	  Writechar(' ');
	  Writechar(BACKSPACE);
	  fflush(stdout);
	}
	else {
	  Writechar(' ');
	  Writechar(BACKSPACE);
	  fflush(stdout);
	}

	do {
	  ch = getchar();

	  /* the following is converted from a case statement to
	     allow the variable characters (backspace, kill_line
	     and break) to be processed.  Case statements in
	     C require constants as labels, so it failed ...
	  */

	    if (ch == backspace) {
              if (index > 0) {
		index--;
		Writechar(BACKSPACE);
		Writechar(' ');
		Writechar(BACKSPACE);
	        fflush(stdout);
	      }
	      else {
		Writechar(' ');
		Writechar(BACKSPACE);
	        fflush(stdout);
	      }
	    }
	    else if (ch == '\n' || ch == '\r') {
	      string[index] = '\0';
	      if (cursor_control)
	        transmit_functions(ON);
	      return(0);
	    }
	    else if (ch == kill_line) {
	      if (mail_only)
	        back_up(index+1);
	      else
	        MoveCursor(x,y);
              CleartoEOLN();
	      index = 0;
	    }
	    else if (ch == NULL) {
	      if (cursor_control)
	        transmit_functions(ON);
	      fflush(stdin); 	/* remove extraneous chars, if any */
	      string[0] = '\0'; /* clean up string, and... */
	      return(-1);
	    }
	    else {  /* default case */
		        
	      string[index++] = ch;
	      Writechar(ch);
	   }
	} while (index < SLEN);

	string[index] = '\0';

	if (cursor_control)
	  transmit_functions(ON);
	return(0);
}


int
pattern_enter(string, alt_string, x, y, alternate_prompt)
char *string, *alt_string, *alternate_prompt;
int  x,y;
{
	/** This function is functionally similar to the routine
	    optionally-enter, but if the first character pressed
	    is a '/' character, then the alternate prompt and string
	    are used rather than the normal one.  This routine 
	    returns 1 if alternate was used, 0 if not
	**/

	char ch;
	register index = 0;

	PutLine1(x, y, "%s", string);	
	CleartoEOLN();
	MoveCursor(x,y);

	if (cursor_control)
	  transmit_functions(OFF);

	ch = getchar();

	if (ch == '\n' || ch == '\r') {
	  if (cursor_control)
	    transmit_functions(ON);
	  return(0);	/* we're done.  No change needed */
	}
	
	if (ch == '/') {
	  PutLine1(x, 0, "%s", alternate_prompt);
	  CleartoEOLN();
	  (void) optionally_enter(alt_string, x, strlen(alternate_prompt)+1,
		 FALSE);
	  return(1);
	}

	CleartoEOLN();

	index = 0;

	if (ch == kill_line) {
	  MoveCursor(x,y);
          CleartoEOLN();
	  index = 0;
	}
	else if (ch != backspace) {
	  Writechar(ch);
	  string[index++] = ch;
	}
	else if (index > 0) {
	  index--;
	  Writechar(BACKSPACE);
	  Writechar(' ');
	  Writechar(BACKSPACE);
	}
	else {
	  Writechar(' ');
	  Writechar(BACKSPACE);
	}

	do {
	  fflush(stdout);
	  ch = getchar();

	  /* the following is converted from a case statement to
	     allow the variable characters (backspace, kill_line
	     and break) to be processed.  Case statements in
	     C require constants as labels, so it failed ...
	  */

	    if (ch == backspace) {
              if (index > 0) {
		index--;
		Writechar(BACKSPACE);
		Writechar(' ');
		Writechar(BACKSPACE);
	      }
	      else {
		Writechar(' ');
		Writechar(BACKSPACE);
	      }
	    }
	    else if (ch == '\n' || ch == '\r') {
	      string[index] = '\0';
	      if (cursor_control)
	        transmit_functions(ON);
	      return(0);
	    }
	    else if (ch == kill_line) {
	      MoveCursor(x,y);
              CleartoEOLN();
	      index = 0;
	    }
	    else if (ch == NULL) {
	      if (cursor_control)
	        transmit_functions(ON);
	      fflush(stdin); 	/* remove extraneous chars, if any */
	      string[0] = '\0'; /* clean up string, and... */
	      return(-1);
	    }
	    else {  /* default case */
		        
	      string[index++] = ch;
	      Writechar(ch);
	   }
	} while (index < SLEN);

	string[index] = '\0';

	if (cursor_control)
	  transmit_functions(ON);
	return(0);
}

back_up(spaces)
int spaces;
{
	/** this routine is to replace the goto x,y call for when sending
	    mail without starting the entire "elm" system up... **/
	
	while (spaces--) {
	  Writechar(BACKSPACE);
	  Writechar(' ');
	  Writechar(BACKSPACE);
	}

	fflush(stdout);
}

int
GetPrompt()
{
	/** This routine does a read/timeout for a single character.
	    The way that this is determined is that the routine to
	    read a character is called, then the "errno" is checked
	    against EINTR (interrupted call).  If they match, this
	    returns NO_OP_COMMAND otherwise it returns the normal
	    command.	
	**/

	int ch;

	if (timeout > 0) {
	  alarm(timeout);
	  errno = 0;	/* we actually have to do this.  *sigh*  */
	  ch = ReadCh();
	  if (errno == EINTR) ch = NO_OP_COMMAND;
	  alarm(0);
	}
	else
	  ch = ReadCh();

	return(ch);
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 7235 ]
  then
    echo $filename changed - should be 7235 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

echo end of this archive file....
exit 0