[mod.sources] Msg Shar.part.3

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

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

# Msg Shar part 3 of 7

# Shell Archive created by hpcnou!dat at Wed Feb 26 15:56:06 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/addr_utils.c src/alias.c         src/aliasdb.c       src/aliaslib.c
#  src/args.c       src/curses.c        src/date.c          src/delete.c
#  src/encode.c     src/file.c


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

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


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

echo extracting file src/addr_utils.c...
cat << 'END-OF-FILE' > src/addr_utils.c
/**			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();

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;

	dprint1("talk-to(sitename='%s')\n", sitename);

	sysname = talk_to_sys;

	if (sysname == NULL) {
	  dprint0("\tWarning: talk_to_sys is currently set to NULL!\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;

	dprint1("remove_domains(host='%s')\n", host);

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

	dprint3("add_site(buffer='%s', site='%s', lastsite='%s')\n", 
	         buffer, site, lastsite);

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

	dprint2("get_address_from(prefix='%s', line='%s', <buffer>)\n", 
		 prefix, 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;
	
	dprint1("translate_return(addr='%s', <return buffer>)\n", addr);

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

	dprint1("\treturning address: '%s'\n", ret_addr);
}

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

	dprint1("build_address(to='%s', <buffer>)\n", to);

	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");
	      leave(1);
	    }
	    else {
	      dprint1("-- Unknown address '%s'\n", buffer);
	      sprintf(buffer, "'%s' is an unknown address.  Replace with: ", 
	              word);
	      word[0] = '\0';
	      if (mail_only) 
	         printf("%s", buffer);
	      else
	        PutLine(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);
	      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[SLEN], timebuff[SLEN];
	int  eight_fields = 0;

	dprint1("real_from(buffer='%s', <header_rec>)\n", buffer);

	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] != ':') 
	  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')
	    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, entry->from, entry->dayname, entry->month, 
                    entry->day, entry->time, entry->year);
	else
	  sscanf(buffer, "%s %s %s %s %s %s %s",
	            junk, entry->from, entry->dayname, entry->month, 
                    entry->day, entry->time, entry->year);
	
	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];

	dprint1("forwarded(buffer='%s', <header_rec>)\n", buffer);

	machine[0] = '\0';

	sscanf(buffer, "%*s %s %s %s %s %s %s %*s %*s %s",
	            entry->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",
	            entry->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",
	            entry->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, entry->from);

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

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 <hpcnou!dat>
	    or  From: hpcnou!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)
	**/

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

	dprint1("parse_arpa_from(buffer='%s', <old from>)\n", buffer);

	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)
	    strcpy(newfrom, temp);
	}
}

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, *strtok();
	int  words = 0;

	dprint1("parse_arpa_date(string='%s', <header_rec>)\n", string);

	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! */
	  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]))
	    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]))
	    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]))
	      return;
	    strncpy(entry->year,  word[5], 4);
	    strncpy(entry->time,  word[4], 10);
	  }
	  else {				       /*** type four!  ***/ 
	    if (! valid_date(word[3], word[2], word[4]))
	      return;
	    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;

	dprint1("fix_arpa_address(%s)\n", address);

	/*  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) {
	       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]);

	dprint1("\tmodified to address '%s'\n", address);

	return(0);
}
END-OF-FILE

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

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

chmod 644 src/addr_utils.c

# ---------- file src/alias.c ----------


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

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

/** This file contains alias stuff

    (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"

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

read_alias_files()
{
	/** read the system and user alias files, if present.
	    Set the flags 'systemfiles' and 'userfiles' accordingly.
	**/

	char fname[SLEN];
	int  hash;

	dprint0("read_alias_files()\n");

	if ((hash = open(system_hash_file, O_RDONLY)) == -1) 
	  goto user;

	read(hash, system_hash_table, sizeof system_hash_table);
	close(hash);

	/* and data file opened.. */

	if ((system_data = open(system_data_file, O_RDONLY)) == -1) 
	  goto user;

	system_files++;	/* got the system files! */

user:   sprintf(fname,  "%s/.alias_hash", home); 

	if ((hash = open(fname, O_RDONLY)) == -1) 
	  return;

	read(hash, user_hash_table, sizeof user_hash_table);
	close(hash);

	sprintf(fname,  "%s/.alias_data", home); 

	if ((user_data = open(fname, O_RDONLY)) == -1) 
	  return;

	user_files++;	/* got user files too! */
}

int
add_alias()
{
	/** add an alias to the user alias text file.  Return zero 
	    if alias not added in actuality **/

	char name[SLEN], *address, address1[LONG_STRING];
	char comment[SLEN];

	dprint0("add_alias()\n");

	PutLine(LINES-2,0,"Enter alias name: ");
	CleartoEOLN();
	Raw(OFF);
	gets(name, 20);
	Raw(ON);
	if ((address = (char *) get_alias_address(name, 0, 0)) != NULL) {
	  if (address[0] == '!') {
	    address[0] = ' ';
	    error1("already a group with that name:%s", address);
	  }
	  else
	    error1("already an alias for that: %s", address);
	  return(0);
	}
	PutLine(LINES-2,0,"Full name for %s: ", name);
	CleartoEOLN();
	Raw(OFF);
	gets(comment,SLEN);
	Raw(ON);
	PutLine(LINES-2,0,"Enter address for %s: ",name);
	CleartoEOLN();
	Raw(OFF);
	gets(address1,LONG_SLEN);
	Raw(ON);
	add_to_alias_text(name, comment, address1);
	return(1);
}

int
add_current_alias()
{
	/** alias the current message to the specified name and
	    add it to the alias text file, for processing as
	    the user leaves the program.  Returns non-zero iff
	    alias actually added to file **/

	char name[SLEN], address1[LONG_STRING], *address;
	char comment[SLEN];

	dprint0("add_current_alias()\n");

	if (current == 0) {
	  error("No message to alias to!");
	  return(0);
	}
	
	PutLine(LINES-2,0,"Current message address aliased to: ");
	CleartoEOLN();
	Raw(OFF);
	gets(name, 20);
	Raw(ON);
	if ((address = (char *) get_alias_address(name, 0, 0)) != NULL) {
	  if (address[1] == '!') {
	    address[0] = ' ';
	    error1("already a group with that name:%s", address);
	  }
	  else 
	    error1("already an alias for that: %s", address); 
          return(0);
	}
	PutLine(LINES-2,0,"Full name of %s: ", name);
	CleartoEOLN();
	Raw(OFF);
	gets(comment, 40);
	Raw(ON);
	get_return(address1);	/* grab the return address of this message */
	optimize_return(address1);
	PutLine(LINES-2,0,"%s (%s) = %s", comment, name, address1);
	CleartoEOLN();
	add_to_alias_text(name, comment, address1);
	return(1);
}

add_to_alias_text(name, comment, address)
char *name, *comment, *address;
{
	/** add the data to the user alias text file... **/
	
	FILE *file;
	char fname[SLEN];
	
	dprint3("add_to_alias_text(name='%s', comment='%s', address='%s')\n", 
		 name, comment, address);

	sprintf(fname,"%s/.alias_text", home);
	
	if ((file = fopen(fname, "a")) == NULL)
	  return(error1("couldn't open %s to add new alias!", fname));

	fprintf(file,"%s : %s : %s\n", name, comment, address);
	fclose(file);

	chown(fname, userid, getgid());

	return(0);
}

show_alias_menu()
{
	MoveCursor(LINES-7,0); CleartoEOLN();	
	MoveCursor(LINES-6,0); CleartoEOLN();	
	MoveCursor(LINES-5,0); CleartoEOLN();
	
	PutLine(LINES-7,COLUMNS-45, "Alias commands");
	Centerline(LINES-5,
"A)lias current msg, Check a P)erson or S)ystem, M)ake new alias, or R)eturn"
	);
}

alias()
{
	/** work with alias commands... **/
	char name[NLEN], *address, ch, buffer[SLEN];
	int  newaliases = 0;

	dprint0("alias()\n");

	if (mini_menu)
	  show_alias_menu();

	define_softkeys(ALIAS);

	while (1) {
	  PutLine(LINES-3,0,"Alias: ");
	  CleartoEOLN();
	  ch = ReadCh();
	  MoveCursor(LINES-1,0); CleartoEOLN();
	  
	  switch (tolower(ch)) {
	    case 'a': newaliases += add_current_alias();	break;
	    case 'm': newaliases += add_alias(); 		break;

	    case RETURN:
	    case 'q':
	    case 'x':
	    case 'r': if (newaliases) install_aliases();
		      return;
	    case 'p': if (newaliases)
			error("Warning: new aliases not installed yet!");
		      PutLine(LINES-2,0,"Check for person: ");
		      CleartoEOLN();
		      Raw(OFF);
	              gets(name,NLEN);
		      Raw(ON);
		      if ((address = get_alias_address(name, 0, 0))!=NULL) {
	                if (address[0] == '!') {
	                  address[0] = ' ';
	                  PutLine(LINES-1,0,"Group alias:%-65.65s", address);
	                  CleartoEOLN();
		        }
			else
			  PutLine(LINES-1,0,"Aliased addresss: %-65.65s", 
			  address);
		      }
	              else
			error("not found");
		      break;

	    case 's': PutLine(LINES-2,0,"Check for system: ");
		      CleartoEOS();
		      Raw(OFF);
	              gets(name,NLEN);
		      Raw(ON);
		      sprintf(buffer, "(user)@%s", name);
	 	      address = (char *) expand_system(buffer, 0, FALSE);
		      if (strlen(address) > strlen(name) + 7)
		        PutLine(LINES-1,0,"Address is: %.65s", address);
		      else
		        error1("couldn't expand system '%s'", name);
		      break;

	    case '@': PutLine(LINES-2,0,"Fully expand alias: ");
		      CleartoEOS();
		      Raw(OFF);
	              gets(name,NLEN);
		      Raw(ON);
		      if ((address = get_alias_address(name, 1, 0)) != NULL) {
	                ClearScreen();
			PutLine(3,0,"Aliased address:\n\r%s", address);
	                PutLine(LINES-1,0,"Press <return> to continue: ");
			(void) getchar();
		      }
	              else
			error("not found");
		      break;
	    default : error("Invalid input!");
	  }
	}
}

install_aliases()
{
	/** run the 'newalias' program and install the newly
	    added aliases before going back to the main
	    program! 
	**/

	dprint0("install_aliases()\n");

	error("Adding new aliases...");
	if (system_call(newalias, SH) == 0) {
	  error("Re-reading the database in...");
	  read_alias_files();
	  set_error("New aliases installed successfully");
	}
	else
	  set_error("'newalias' failed.  Please check alias_text");
}
END-OF-FILE

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

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

chmod 644 src/alias.c

# ---------- file src/aliasdb.c ----------


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

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

/** Alias database files...

    (C) Copyright 1986 Dave Taylor
**/


#include "headers.h"

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

char *shift_lower();

findnode(name, display_error)
char *name;
int   display_error;
{
	/** break 'name' into machine!user or user@machine and then
	    see if you can find 'machine' in the path database..
	    If so, return name as the expanded address.  If not,
	    return what was given to us!   If display_error, then
	    do so...
	**/

	char   address[SLEN];
	
	dprint2("findnode(name='%s', display_error=%s)\n", 
		 name, display_error? "ON" : "OFF");

	if (strlen(name) == 0)
	  return;
	
	if (expand_site(name, address) == -1) {
	  if (display_error && name[0] != '!') {
	    error1("Warning: couldn't expand %s...", name);
	    sleep(1);
	  }
	}
	else
	  strcpy(name, address);
	
	return;
}

int
expand_site(cryptic, expanded)
char *cryptic, *expanded;
{

	/** Given an address of the form 'xyz@site' or 'site!xyz'
	    return an address of the form <expanded address for site>
            with 'xyz' embedded according to the path database entry.
	    Note that 'xyz' can be eiher a simple address (as in "joe")
	    or a complex address (as in "joe%xerox.parc@Xerox.ARPA")!
	    0 = found, -1 return means unknown site code **/

	char   name[VERY_LONG_STRING], sitename[VERY_LONG_STRING], 
               address[VERY_LONG_STRING], temp[VERY_LONG_STRING];
	register int i = 0, j = 0, domain_name;

	dprint1("expand_site(cryptic='%s', <expanded>)\n", cryptic);

	/** break down **/

	while (cryptic[i] != '@' && cryptic[i] != '!' && cryptic[i] != '\0')
	  sitename[j++] = cryptic[i++];

	sitename[j++] = '\0';

	j = 0;
	
	if (cryptic[i] == '\0') return(-1);	/* nothing to expand! */

	domain_name = (cryptic[i] == '@');

	i++;

	while (cryptic[i] != '\0')
	  name[j++] = cryptic[i++];

	name[j] = '\0';

	if (domain_name) {
	  strcpy(temp, name);
	  strcpy(name, sitename);
	  strcpy(sitename, temp);
	}

	if (talk_to(sitename)) {
	  sprintf(expanded,"%s!%s", sitename, name);
	  return(0);
	}

	if (size_of_pathfd > 0) {
          if (binary_search(sitename, address) == -1)
	    return(-1);
	}
	else {
	  sprintf(expanded,"%s!%s", sitename, name);
	  return(0);
	}

	/* otherwise... */

	sprintf(expanded, address, name);
	return(0);
}

int
binary_search(name, address)
char *name, *address;
{
	/* binary search file for name.  Return 0 if found, -1 if not */

	char machine[20];
	register long first = 0, last, middle;
	register int  compare;

	dprint1("binary_search(name='%s', <address>)\n", name);

	address[0] = '\0';

	last = size_of_pathfd;

	do {

	  middle = (long) ((first+last) / 2);

	  get_entry(machine, address, pathfd, middle);

	  compare = strcmp(name, machine);

	  if (compare < 0) 
	    last = middle - 1;
	  else if (compare == 0) 
	    return(0);
	  else  /* greater */
	    first = middle + 1; 
	} while (abs(last) - abs(first) > FIND_DELTA);

	return(-1);
}

get_entry(machine, address, fileid, offset)
char *machine, *address;
FILE *fileid;
long offset;
{
	/** get entry...return machine and address immediately
	    following given offset in fileid.  **/

	fseek(fileid, offset, 0L);

	/* read until we hit an end-of-line */

	while (getc(fileid) != '\n')
	   ;

	fscanf(fileid, "%s\t%s", machine, address);
}

init_findnode()
{
	/** Initialize the FILE and 'size_of_file' values for the 
	    findnode procedure **/

	struct stat buffer;

	dprint0("init_findnode()\n");

	if (stat(pathfile, &buffer) == -1) {
	  dprint1("\tWarning: No pathalias file '%s' found!\n", pathfile);
	  size_of_pathfd = 0;
	  return;
	}

	size_of_pathfd = buffer.st_size;

	if ((pathfd = fopen(pathfile,"r")) == NULL) {
	  dprint1("\tFound pathalias file '%s' but couldn't open to read\n",
		  pathfile);
	  size_of_pathfd = 0;
	}
	else
	  dprint1("\tOpened file '%s' as path alias database\n", pathfile);
}
END-OF-FILE

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

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

chmod 644 src/aliasdb.c

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


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

echo extracting file src/aliaslib.c...
cat << 'END-OF-FILE' > src/aliaslib.c
/**			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();

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;

	dprint3("get_alias_address(name='%s', mailing=%s, depth=%d)\n", 
		 name, mailing? "ON" : "OFF", depth);

	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( (char *) expand_group(buffer, depth));
	    else
	      return( (char *) expand_system(buffer, depth, TRUE));
	  }
	 
	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( (char *) expand_group(buffer, depth));
	    else
	      return( (char *) expand_system(buffer, depth, TRUE));
	  }
	
	return( (char *) NULL);
}

char *expand_system(buffer, depth, show_errors)
char *buffer;
int   depth, 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("expand_system(buffer='%s', show_errors=%s)\n", 
	         buffer, show_errors? "ON" : "OFF");

	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;

	dprint2("expand_group(members='%s', depth=%d)\n", members, depth);

	strcpy(buf, members); 	/* parameter safety! */
	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)) {
	      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)
	  exit(printf("Bad alias name: %s.  Too long.\n", word));

	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

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

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

chmod 644 src/aliaslib.c

# ---------- file src/args.c ----------


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

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

/**			args.c			**/

/** starting argument parsing routines for MSG system...

    (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"

#define DONE		0
#define ERROR		-1

char *optional_arg;			/* optional argument as we go */
int   opt_index;			/* argnum + 1 when we leave   */

parse_arguments(argc, argv, to_whom)
int argc;
char *argv[], *to_whom;
{
	/** set flags according to what was given to program.  If we are 
	    fed a name or series of names, put them into the 'to_whom' buffer
	    and set "mail_only" to TRUE **/

	register char c = 0;

	infile[0] = '\0';
	to_whom[0] = '\0';
	batch_subject[0] = '\0';

	while ((c = get_options(argc, argv, "?cdf:hkmsS:z")) > 0) {
	   switch (c) {
	     case 'c' : check_only++;		break;
	     case 'd' : debug++;		break;
	     case 'f' : strcpy(infile, optional_arg); 
	                mbox_specified = 2;  break;
	     case '?' :
	     case 'h' : args_help();
	     case 'k' : hp_terminal++;	break;
	     case 'm' : mini_menu = 0;	break;
	     case 's' : hp_softkeys++;	break;
	     case 'S' : strcpy(batch_subject, optional_arg);	break;
	     case 'z' : check_mailfile_size(); break;
	    }
	 }

	 if (c == ERROR) {
	   printf("Usage: msg [cdhkmsz] [-f file] [-S subject] <names>\n\n");
	   args_help();
	}

	 if (opt_index < argc) 
	   while (opt_index < argc) {
	     sprintf(to_whom, "%s%s%s", to_whom, 
		    to_whom[0] != '\0'? " " : "", argv[opt_index]);
	     mail_only++;
	     opt_index++;
	   }

	 if (strlen(batch_subject) > 0 && ! mail_only) 
	   exit(printf(
     "\n\rDon't understand specifying a subject and no-one to send to!\n\r"));

	if (! isatty(fileno(stdin)) && strlen(batch_subject) == 0)
	  strcpy(batch_subject, DEFAULT_BATCH_SUBJECT);
		
}

args_help()
{
	/**  print out possible starting arguments... **/

	printf("\nPossible Starting Arguments for MSG program:\n\n");
	printf("\targ\t\t\tMeaning\n");
	printf("\t -c \t\tCheckalias - check the given aliases only\n");
	printf("\t -d \t\tDebug - turn on debug mode (output to file)\n");
	printf("\t -f \t\tFile - read file (specified) rather than mailbox\n");
	printf("\t -h \t\tHelp - give this list of options\n");
	printf("\t -k \t\tKeypad - force knowledge of HP terminal keyboard\n");
	printf("\t -m \t\tMenu - Turn off menu, using more of the screen\n");
	printf("\t -s \t\tSoftkeys - enable use of softkeys\n");
	printf("\t -z \t\tZero - don't enter MSG if no mail is pending\n");
	printf("\n");
	printf("\t -S \t\tSubject - for batchmailing, combined with address\n");
	printf("\n");
	exit(1);
}

int  _indx = 1, _argnum = 1;

int
get_options(argc, argv, options)
int argc;
char *argv[], *options;
{
	/** Returns the character argument next, and optionally instantiates 
	    "argument" to the argument associated with the particular option 
	**/
	
	char       *word, *strchr();

	if (_indx >= strlen(argv[_argnum])) {
	  _argnum++;
	  _indx = 1;		/* zeroeth char is '-' */
	}

	if (_argnum >= argc) {
	  opt_index = argc;
	  return(DONE);
	}
	
	if (argv[_argnum][0] != '-') {
	  opt_index = _argnum;
	  return(DONE);
	}

        word = strchr(options, argv[_argnum][_indx++]);

	if (strlen(word) == 0) 
	  return(ERROR);
	
	if (word[1] == ':') {

	  /** Two possibilities - either tailing end of this argument or the 
	      next argument in the list **/

	  if (_indx < strlen(argv[_argnum])) { /* first possibility */
	    optional_arg = (char *) (argv[_argnum] + _indx);
	    _argnum++;
	    _indx = 1;
	  }
	  else {				/* second choice     */
	    if (++_argnum >= argc) 
	      return(ERROR);			/* no argument!!     */

	    optional_arg = (char *) argv[_argnum++];
	    _indx = 1;
	  }
	}

	return((int) word[0]);
}
END-OF-FILE

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

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

chmod 644 src/args.c

# ---------- file src/curses.c ----------


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

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

/**  This library gives programs the ability to easily access the
     termcap information and write screen oriented and raw input
     programs.  The routines can be called as needed, except that
     to use the cursor / screen routines there must be a call to
     InitScreen() first.  The 'Raw' input routine can be used
     independently, however.

     Modified 2/86 to work (hopefully) on Berkeley systems.  If
     there are any problems with BSD Unix, please report them to
     the author at hpcnoe!dat@HPLABS (fixed, if possible!)

     (C) Copyright 1985 Dave Taylor, HP Colorado Networks
**/

#include <stdio.h>

#ifdef RAWMODE
# ifdef BSD
#  include <sgtty.h>
# else
#  include <termio.h>
# endif
#endif

#include <ctype.h>

#ifdef BSD
#undef tolower
#endif
#include "curses.h"

#ifdef BSD
# include "/usr/include/curses.h"  	/* don't ask! */
#endif

#ifdef RAWMODE
# define TTYIN	0
#endif

extern int debug;

#ifdef RAWMODE
#  ifndef BSD
    struct termio _raw_tty, 
                _original_tty;
#  endif

static int _inraw = 0;                  /* are we IN rawmode?    */

#endif

static int _intransmit;			/* are we transmitting keys? */

static
char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
     *_setbold, *_clearbold, *_setunderline, *_clearunderline, 
     *_sethalfbright, *_clearhalfbright, *_setinverse, *_clearinverse,
     *_cleartoeoln, *_cleartoeos, *_transmit_on, *_transmit_off;
static
int
     _lines, _columns;

static char _terminal[1024];              /* Storage for terminal entry */
static char _capabilities[256];           /* String for cursor motion */

static char *ptr = _capabilities;	/* for buffering         */

int    outchar();			/* char output for tputs */

InitScreen()
{
   /* Set up all this fun stuff: returns zero if all okay, or;
        -1 indicating no terminal name associated with this shell,
        -2..-n  No termcap for this terminal type known
   */

   int  tgetent(),      /* get termcap entry */
        error;
   char *tgetstr(),     /* Get termcap capability */
        termname[40];

   if (strcpy(termname, getenv("TERM")) == NULL)
     return(-1);

   if ((error = tgetent(_terminal, termname)) != 1)
     return(error-2);

   /* load in all those pesky values */
   _clearscreen       = tgetstr("cl", &ptr);
   _moveto            = tgetstr("cm", &ptr);
   _up                = tgetstr("up", &ptr);
   _down              = tgetstr("do", &ptr);
   _right             = tgetstr("nd", &ptr);
   _left              = tgetstr("bs", &ptr); 
   _setbold           = tgetstr("so", &ptr);
   _clearbold         = tgetstr("se", &ptr);
   _setunderline      = tgetstr("us", &ptr);
   _clearunderline    = tgetstr("ue", &ptr);
   _setinverse        = tgetstr("so", &ptr);
   _clearinverse      = tgetstr("se", &ptr);
   _sethalfbright     = tgetstr("hs", &ptr);
   _clearhalfbright   = tgetstr("he", &ptr);
   _cleartoeoln       = tgetstr("ce", &ptr);
   _cleartoeos        = tgetstr("cd", &ptr);
   _lines	      = tgetnum("li");
   _columns	      = tgetnum("co");
   _transmit_on	      = tgetstr("ks", &ptr);
   _transmit_off      = tgetstr("ke", &ptr);


   if (!_left) {
      _left = ptr;
      *ptr++ = '\b';
      *ptr++ = '\0';
   }

#ifdef BSD
	initscr();	/* initalize curses too! */
#endif

   return(0);
}

char *return_value_of(termcap_label)
char *termcap_label;
{
	/** this will return the string kept by termcap for the 
	    specified capability **/

   	char *tgetstr();     		/* Get termcap capability */

	return( (char *) tgetstr(termcap_label, &ptr));
}

transmit_functions(newstate)
int newstate;
{
	/** turn function key transmission to ON | OFF **/
	
	if (newstate != _intransmit) {
	  _intransmit = ! _intransmit;
	  if (newstate == ON)
   	    tputs(_transmit_on, 1, outchar);
	  else 
   	    tputs(_transmit_off, 1, outchar);

   	  fflush(stdout);      /* clear the output buffer */
	}
}

/****** now into the 'meat' of the routines...the cursor stuff ******/

ScreenSize(lines, columns)
int *lines, *columns;
{
	/** returns the number of lines and columns on the display. **/

	*lines = _lines - 1;		/* assume index from zero */
	*columns = _columns;
}

ClearScreen()
{
        /* clear the screen: returns -1 if not capable */

   if (!_clearscreen) 
     return(-1);

   tputs(_clearscreen, 1, outchar);
   fflush(stdout);      /* clear the output buffer */
   return(0);
}

MoveCursor(row, col)
int row, col;
{
        /** move cursor to the specified row column on the screen.
            0,0 is the top left! **/

           char *tgoto();
	   char *stuff;

           if (!_moveto) 
             return(-1);

           stuff = (char *) tgoto(_moveto, col, row);
	   tputs(stuff, 1, outchar);
           fflush(stdout);
           return(0);
}


CursorUp()
{
        /** move the cursor up one line **/

        if (!_up)
           return(-1);

   	tputs(_up, 1, outchar);
	fflush(stdout);
        return(0);
}


CursorDown()
{
        /** move the cursor down one line **/

       if (!_down) 
          return(-1);

       tputs(_down, 1, outchar);
       fflush(stdout);
       return(0);
}


CursorLeft()
{
        /** move the cursor one character to the left **/

       if (!_left) 
          return(-1);

       tputs(_left, 1, outchar);
       fflush(stdout);
       return(0);
}


CursorRight()
{
        /** move the cursor one character to the right (nondestructive) **/

       if (!_right) 
          return(-1);

       tputs(_right, 1, outchar);
       fflush(stdout);
       return(0);
}


StartBold()
{
        /** start boldface/standout mode **/

       if (!_setbold) 
         return(-1);

       tputs(_setbold, 1, outchar);
       fflush(stdout);
       return(0);
}


EndBold()
{
        /** compliment of startbold **/

        if (!_clearbold) 
           return(-1);

       tputs(_clearbold, 1, outchar);
       fflush(stdout);
       return(0);
}


StartUnderline()
{
        /** start underline mode **/

       if (!_setunderline) 
          return(-1);

       tputs(_setunderline, 1, outchar);
       fflush(stdout);
       return(0);
}


EndUnderline()
{
        /** the compliment of start underline mode **/

       if (!_clearunderline) 
          return(-1);

       tputs(_clearunderline, 1, outchar);
       fflush(stdout);
       return(0);
}


StartHalfbright()
{
        /** start half intensity mode **/

       if (!_sethalfbright) 
         return(-1);

       tputs(_sethalfbright, 1, outchar);
       fflush(stdout);
       return(0);
}

EndHalfbright()
{
        /** compliment of starthalfbright **/

       if (!_clearhalfbright) 
          return(-1);

       tputs(_clearhalfbright, 1, outchar);
       fflush(stdout);
       return(0);
}

StartInverse()
{
        /** set inverse video mode **/

       if (!_setinverse) 
         return(-1);

       tputs(_setinverse, 1, outchar);
       fflush(stdout);
       return(0);
}


EndInverse()
{
        /** compliment of startinverse **/

       if (!_clearinverse) 
         return(-1);

       tputs(_clearinverse, 1, outchar);
       fflush(stdout);
       return(0);
}

PutLine(x, y, line, args)
int x,y;
char *line; 
int  args;
{
        /** write line at location x,y **/

        MoveCursor(x,y);
	_doprnt(line, &args, stdout);
        fflush(stdout);    /* ensure it actually gets out! */
}

CleartoEOLN()
{
        /** clear to end of line **/

       if (!_cleartoeoln) 
         return(-1);

       tputs(_cleartoeoln, 1, outchar);
       fflush(stdout);  /* clear the output buffer */
       return(0);
}

CleartoEOS()
{
        /** clear to end of screen **/

       if (!_cleartoeos) 
         return(-1);

       tputs(_cleartoeos, 1, outchar);
       fflush(stdout);  /* clear the output buffer */
       return(0);
}

#ifdef RAWMODE

Raw(state)
int state;
{
	/** state is either ON or OFF, as indicated by call **/

        if (state == OFF && _inraw) {
#ifdef BSD
	  echo();
	  nocrmode();
#else
	  (void) ioctl(TTYIN, TCSETAW, &_original_tty);
#endif
          _inraw = 0;
	}
        else if (state == ON && ! _inraw) {
#ifdef BSD
	   noecho();
	   crmode();
#else
	  (void) ioctl(TTYIN, TCGETA, &_original_tty);	/** current setting **/
	  	
	  (void) ioctl(TTYIN, TCGETA, &_raw_tty);    /** again! **/
	  _raw_tty.c_iflag &= ~(INLCR | ICRNL |BRKINT);
	  _raw_tty.c_iflag |= IXON;
	  _raw_tty.c_oflag |= OPOST;
	  _raw_tty.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
	  _raw_tty.c_lflag &= ~(ICANON | ECHO);
	  _raw_tty.c_cc[VMIN] = '\01';
	  _raw_tty.c_cc[VTIME] = '\0';
	  (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
#endif

          _inraw = 1;
        }
}

int
ReadCh()
{
        /** read a character with Raw mode set! **/

        register int result;
        char ch;

        result = read(0, &ch, 1);
	
	return(result == 0? EOF : ch);
}

#endif

outchar(c)
char c;
{
	/** output the given character.  From tputs... **/
	/** Note: this CANNOT be a macro!              **/

	putc(c, stdout);
}
END-OF-FILE

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

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

chmod 644 src/curses.c

# ---------- file src/date.c ----------


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

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

/** return the current date and time in a readable format! **/
/** also returns an ARPA RFC-822 format date...            **/

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

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

#include <ctype.h>

#ifdef BSD
#undef toupper
#endif

#define MONTHS_IN_YEAR	11	/* 0-11 equals 12 months! */
#define FEB		 1	/* 0 = January 		  */
#define DAYS_IN_LEAP_FEB 29	/* leap year only 	  */

#define ampm(n)		(n > 12? n - 12 : n)
#define am_or_pm(n)	(n > 11? (n > 23? "am" : "pm") : "am")
#define leapyear(year)	((year % 4 == 0) && (year % 100 != 0))

char *dayname[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
		  "Friday", "Saturday", "" };

char *monname[] = { "January", "February", "March", "April", "May", "June",
		  "July", "August", "September", "October", "November",
		  "December", ""};

char *arpa_dayname[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
		  "Fri", "Sat", "" };

char *arpa_monname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
		  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""};

int  days_in_month[] = { 31,    28,    31,    30,    31,     30, 
		  31,     31,    30,   31,    30,     31,  -1};

#ifdef BSD
  char *tzname();
#else
  extern char *tzname[];
#endif

char *get_date()
{
	/** return the date in the format exemplified by;
		Thursday, April 18th 1985 at 8:35 pm
	**/

	static char buffer[SLEN];	/* static character buffer       */
	struct tm *the_time,		/* Time structure, see CTIME(3C) */
	          *localtime();
	char      *suffix();	 	/* digit suffix for date	 */
	long	  junk;			/* time in seconds....		 */

	dprint0("get_date()\n");

	junk = time(0);	/* this must be here for it to work! */
	the_time = localtime(&junk);
		
	sprintf(buffer, "%s, %s %d%s %d at %d:%02d %s",
		 dayname[the_time->tm_wday],	/* weekday */
		 monname[the_time->tm_mon],	/* month   */
		 the_time->tm_mday,		/* day     */
		 suffix(the_time->tm_mday),	/* suffix  */
		 the_time->tm_year + 1900,	/* year    */
		 ampm(the_time->tm_hour),	/* hour    */
		 the_time->tm_min,		/* minute  */
                 ((the_time->tm_hour == 12 || the_time->tm_hour == 24)
		  && the_time->tm_min == 0) ? (the_time->tm_hour == 12? "noon" :
		  "midnight") : am_or_pm(the_time->tm_hour));	/* am | pm */

	return( (char *) buffer);
}

char *suffix(day)
int day;
{
	/** this routine returns the suffix appropriate for the
	    specified number to make it an ordinal number.  ie,
	    if given '1' it would return 'st', and '2' => 'nd'
	**/

	static char buffer[10];
	register int digit;

	digit = day % 10;

	if (digit == 0 || digit > 3)
	  strcpy(buffer,"th");
	else if (digit == 1)
	  strcpy(buffer,"st");
	else if (digit == 2)
	  strcpy(buffer, "nd");
	else
	  strcpy(buffer, "rd");

	return( (char *) buffer);
}

char *get_arpa_date()
{
	/** returns an ARPA standard date.  The format for the date
	    according to DARPA document RFC-822 is exemplified by;

	       	      Mon, 12 Aug 85 6:29:08 MST

	**/

	static char buffer[SLEN];	/* static character buffer       */
	struct tm *the_time,		/* Time structure, see CTIME(3C) */
		  *localtime();
	long	   junk;		/* time in seconds....		 */

	dprint0("get_arpa_date()\n");

	junk = time(0);	/* this must be here for it to work! */
	the_time = localtime(&junk);

	sprintf(buffer, "%s, %d %s %d %d:%02d:%02d %s",
	  arpa_dayname[the_time->tm_wday],
	  the_time->tm_mday % 32,
	  arpa_monname[the_time->tm_mon],
	  the_time->tm_year % 100,
	  the_time->tm_hour % 24,
	  the_time->tm_min  % 61,
	  the_time->tm_sec  % 61,
#ifdef BSD
	  tzname());
#else
	  tzname[the_time->tm_isdst]);
#endif
	
	return( (char *) buffer);
}

char *full_month(month)
char *month;
{
	/** Given a three letter month abbreviation, return the 
	    full name of the month.   If can't figure it out, just
	    return the given argument. **/

	char   name[4];
	register int i;

	/** ensure name in correct case... **/

	strncpy(name, shift_lower(month), 3);
	name[0] = toupper(name[0]);

	/** now simply step through arpa_monname table to find a match **/

	for (i=0; i < 12; i++)
	  if (strncmp(name, arpa_monname[i], 3) == 0)
	    return((char *) monname[i]);
	
	return( (char *) month);
}

days_ahead(days, buffer)
int days;
char *buffer;
{
	/** return in buffer the date (Day, Mon Day, Year) of the date
	    'days' days after today. **/

	struct tm *the_time,		/* Time structure, see CTIME(3C) */
		  *localtime();
	long	   junk;		/* time in seconds....		 */

	dprint1("days_ahead(days=%d)\n", days);

	junk = time(0);	/* this must be here for it to work! */
	the_time = localtime(&junk);

	/* increment the day of the week */

	the_time->tm_wday = (the_time->tm_wday + days) % 7;

	/* the day of the month... */
	the_time->tm_mday += days;
	
	if (the_time->tm_mday > days_in_month[the_time->tm_mon]) {
	  if (the_time->tm_mon == FEB && leapyear(the_time->tm_year)) {
	    if (the_time->tm_mday > DAYS_IN_LEAP_FEB) {
	      the_time->tm_mday -= days_in_month[the_time->tm_mon];
	      the_time->tm_mon += 1;
	    }
	  }
	  else {
	    the_time->tm_mday -= days_in_month[the_time->tm_mon];
	    the_time->tm_mon += 1;
	  }
	}

	/* check the month of the year */
	if (the_time->tm_mon > MONTHS_IN_YEAR) {
	  the_time->tm_mon -= MONTHS_IN_YEAR;
	  the_time->tm_year += 1;
	}

	/* now, finally, build the actual date string */

	sprintf(buffer, "%s, %d %s %d",
	  arpa_dayname[the_time->tm_wday],
	  the_time->tm_mday % 32,
	  arpa_monname[the_time->tm_mon],
	  the_time->tm_year % 100);
}

int
valid_date(day, mon, year)
char *day, *mon, *year;
{
	/** validate the given date - returns TRUE iff the date
	    handed is reasonable and valid.  **/

	register int daynum, yearnum;

	dprint3("valid_date(day='%s', month='%s', year='%s')\n", day, mon, year);

	daynum = atoi(day);
	yearnum = atoi(year);
	
	if (daynum < 1 || daynum > 31)
	  return(0);
	
	if (yearnum < 1 || (yearnum > 100 && yearnum < 1900) ||
	    yearnum > 2000)
	  return(0);
	
	return(1);
}

fix_date(entry)
struct header_rec *entry;
{
	/** This routine will 'fix' the date entry for the specified
	    message.  This consists of 1) adjusting the year to 0-99
	    and 2) altering time from HH:MM:SS to HH:MM am|pm **/ 

	dprint3("fix_date(month='%s', day='%s', year='%s')\n",
		 entry->month, entry->day, entry->year);

	if (atoi(entry->year) > 99) 	
	  sprintf(entry->year,"%d", atoi(entry->year) - 1900);

	fix_time(entry->time);
}

fix_time(timestring)
char *timestring;
{
	/** Timestring in format HH:MM:SS (24 hour time).  This routine
	    will fix it to display as: HH:MM [am|pm] **/

	int hour, minute;

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

	sscanf(timestring, "%d:%d", &hour, &minute);

	if (hour < 1 || hour == 24) 
	  sprintf(timestring, "12:%2d (midnight)", minute);
	else if (hour < 12)
	  sprintf(timestring, "%d:%2.2d am", hour, minute);
	else if (hour == 12)
	  sprintf(timestring, "%d:%2.2d (noon)", hour, minute);
	else if (hour < 24)
	  sprintf(timestring, "%d:%2.2d pm", hour-12, minute);
}

#ifdef BSD

char *tzname()
{
	/** Return the name of the timezone (three letters) by plowing about
	    in the various time structures on the Berkeley systems.  This is
	    a pretty grungy routine, actually! **/

	struct timeval  *current_time;
	struct timezone *time_zone;	
	char   *timezone();

	gettimeofday(current_time, time_zone);

	return (timezone(time_zone->tz_minuteswest, time_zone->tz_dsttime));
}

#endif
END-OF-FILE

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

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

chmod 644 src/date.c

# ---------- file src/delete.c ----------


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

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

/**  Delete or undelete files: just set flag in header record! 

     (C) Copyright 1985  Dave Taylor
**/

#include "headers.h"

delete(real_del)
int real_del;
{
	/** Delete current message.  If real-del is false, then we're
	    actually requested to toggle the state of the current
	    message... **/

	dprint1("delete(real_del=%s)\n", real_del? "ON" : "OFF");

	if (real_del)
	  header_table[current-1].delete = 1;
	else
	  header_table[current-1].delete = ! header_table[current-1].delete;

	show_msg_status(current-1);
}

undelete()
{
	/** clear the deleted message flag **/

	dprint0("undelete()\n");

	header_table[current-1].delete = 0;

	show_msg_status(current-1);
}

show_delete_flags()
{
	/** display page of headers (10) if present.  First check to 
	    ensure that header_page is in bounds, fixing silently if not **/

	register int first = 0, last = 0, line = 4;

	dprint0("show_delete_flags()\n");

	(void) 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 stuff **/

	while (first <= last) {
	  MoveCursor(line++,7);
	  if (header_table[first].delete) putchar('*');
 	  else				  putchar(' ');
	  first++;
	}
}

show_msg_status(msg)
int msg;
{
	/** show the status of the current message only.  **/

	register int line;

	dprint1("show_msg_status(msg=%d)\n", msg);

	line = (msg % headers_per_page) + 4;

	MoveCursor(line,7);
	putchar( header_table[msg].delete? '*' : ' ');
}
END-OF-FILE

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

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

chmod 644 src/delete.c

# ---------- file src/encode.c ----------


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

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

/** This is a heavily mangled version of the 'cypher' program written by
    person or persons unknown.  

    (C) Copyright 1986, Dave Taylor
**/

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

#define RS	94
#define RN	4
#define RMASK	0x7fff	/* use only 15 bits */

static char r[RS][RN];		/* rotors */
static char ir[RS][RN];		/* inverse rotors */
static char h[RS];		/* half rotor */
static char s[RS];		/* shuffle vector */
static int  p[RN];		/* rotor indices */

static char the_key[SLEN];	/* unencrypted key */
static char *encrypted_key;	/* encrypted key   */

getkey(send)
int send;
{
	/** this routine prompts for and returns an encode/decode
	    key for use in the rest of the program.  If send == 1
	    then need to mess with rawmode. **/

	char buffer[NLEN];
	int gotkey = 0;

	dprint1("getkey(send=%s)\n", send? "ON" : "OFF");

	ClearLine(21);

	if (send) Raw(OFF);

	while ( !gotkey ) {
	  MoveCursor(LINES-1,0);
	  ClearLine(LINES-1);
	  if (send)
	    strcpy( buffer, getpass( "Enter encryption key: "));
	  else
	    strcpy( buffer, getpass( "Enter decryption key: "));
	  MoveCursor(LINES-1,0);
	  if ( strcmp( buffer, getpass( "Please enter it again: "))) {
	    error("Your keys were not the same!");
	    sleep(1);
	    clear_error();
	    continue;
	  }
	  strcpy(the_key, buffer);	/* save unencrypted key */
	  makekey( buffer );
	  gotkey = 1;
	}

	if (send) Raw(ON);

	setup();		/** initialize the rotors etc. **/

	ClearLine(LINES-1);		
	clear_error();
}

get_key_no_prompt()
{
	/** This performs the same action as get_key, but assumes that
	    the current value of 'the_key' is acceptable.  This is used
	    when a message is encrypted twice... **/

	char buffer[SLEN];

	dprint0("get_key_no_prompt()\n");

	strcpy(buffer, the_key);

	makekey( buffer );

	setup();
}

encode(line)
char *line;
{
	/** encrypt or decrypt the specified line.  Uses the previously
	    entered key... **/

	register int i, index, j, ph = 0;

	dprint1("encode(line='%s')\n", line);

	for (index=0; index < strlen(line); index++) {
	  i = (int) line[index];

	  if ( (i >= ' ') && (i < '~') ) {
	    i -= ' ';

	    for ( j = 0; j < RN; j++ )		/* rotor forwards */
	      i = r[(i+p[j])%RS][j];

	    i = ((h[(i+ph)%RS])-ph+RS)%RS;	/* half rotor */

	    for ( j--  ; j >= 0; j-- )		/* rotor backwards */
	      i = (ir[i][j]+RS-p[j])%RS;

	    j = 0;				/* rotate rotors */
	    p[0]++;
	    while ( p[j] == RS ) {
	      p[j] = 0;
	      j++;
	      if ( j == RN ) break;
	      p[j]++;
            }
  
	    if ( ++ph == RS )
	      ph = 0;

	    i += ' ';
	  }
	  
	  line[index] = (char) i;	/* replace with altered one */
	}
}


makekey( rkey)
char *rkey;
{
	/** encrypt the key using the system routine 'crypt' **/

	char key[8], salt[2], *crypt();

	dprint1("makekey(rkey='%s')\n", rkey);

	strncpy( key, rkey, 8);
	salt[0] = key[0];
	salt[1] = key[1];
	encrypted_key = crypt( key, salt);
}

/*
 * shuffle rotors.
 * shuffle each of the rotors indiscriminately.  shuffle the half-rotor
 * using a special obvious and not very tricky algorithm which is not as
 * sophisticated as the one in crypt(1) and Oh God, I'm so depressed.
 * After all this is done build the inverses of the rotors.
 */

setup()
{
	register long i, j, k, temp;
	long seed;

	dprint0("setup()\n");

	for ( j = 0; j < RN; j++ ) {
		p[j] = 0;
		for ( i = 0; i < RS; i++ )
			r[i][j] = i;
	}

	seed = 123;
	for ( i = 0; i < 13; i++)		/* now personalize the seed */
	  seed = (seed*encrypted_key[i] + i) & RMASK;

	for ( i = 0; i < RS; i++ )		/* initialize shuffle vector */
	  h[i] = s[i] = i;

	for ( i = 0; i < RS; i++) {		/* shuffle the vector */
	  seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
	  k = ((seed % 65521) & RMASK) % RS;
	  temp = s[k];
	  s[k] = s[i];
	  s[i] = temp;
	}

	for ( i = 0; i < RS; i += 2 ) {	/* scramble the half-rotor */
	  temp = h[s[i]];			/* swap rotor elements ONCE */
	  h[s[i]] = h[s[i+1]];
	  h[s[i+1]] = temp;
	}

	for ( j = 0; j < RN; j++) {			/* select a rotor */

	  for ( i = 0; i < RS; i++) {		/* shuffle the vector */
	    seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
	    k = ((seed % 65521) & RMASK) % RS;
	    temp = r[i][j];
	    r[i][j] = r[k][j];
	    r[k][j] = temp;
	  }

	  for ( i = 0; i < RS; i++) 		/* create inverse rotors */
	    ir[r[i][j]][j] = i;
       }
}
END-OF-FILE

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

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

chmod 644 src/encode.c

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


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

echo extracting file src/file.c...
cat << 'END-OF-FILE' > src/file.c
/**		file.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

int
save()
{
	/** save current message in file.  Append if possible!
	    Returns zero iff no file specified (cancelled command) **/

	char filename[SLEN], address[LONG_SLEN], buffer[SLEN];
	FILE *save_file;

	dprint0("save()\n");

	MoveCursor(LINES-2,0);
	printf("File current message in: ");

	if (save_by_name) {
	  /** build default filename to save to **/
	  get_return(address);
	  get_return_name(address, buffer);
	  sprintf(filename, "=/%s", buffer);
	}
	else
	  filename[0] = '\0';

	optionally_enter(filename, LINES-2, 25, FALSE);
	MoveCursor(LINES-1,0);

	if (strlen(filename) == 0)  /** <return> means 'cancel', right? **/
	  return(0);

	if (! expand_filename(filename))
	  return(0);	/* failed expanding name! */

	if ((save_file = fopen(filename,"a")) == NULL) {
	  error1("Couldn't append to file %s!", filename);
	  return(0); 
	}

	copy_message("", save_file, FALSE);

	fclose(save_file);

	chown(filename, getuid(), getgid());	/* owned by user */
	error1("Message saved in file %s", filename);
	return(1);
}

int
expand_filename(filename)
char *filename;
{
	/* expands '~' and '=' to specified file names, also will
	   try to expand shell variables if encountered.. */

	char buffer[SLEN], varname[SLEN], env_value[SLEN];
	register int i = 1, index = 0;

	dprint1("expand_filename(filename='%s')\n", filename);

	/** new stuff - make sure no illegal char as last **/

	if (lastch(filename) == '\n' || lastch(filename) == '\r')
	  lastch(filename) = '\0';
	  
	if (filename[0] == '~') {
	  sprintf(buffer, "%s%s%s", home, 
		(filename[1] != '/' && lastch(folders) != '/')? "/" : "",
	  	(char *) filename + 1);
	  strcpy(filename, buffer);
	}
	else if (filename[0] == '=') {
	  if (strlen(folders) == 0) {
	    error("MAILDIR not defined.  Can't expand '='");
	    return(0);
	  }
	  sprintf(buffer, "%s%s%s", folders, 
		(filename[1] != '/' && lastch(folders) != '/')? "/" : "",
		(char *) filename + 1);
	  strcpy(filename, buffer);
	}
	else if (filename[0] == '$') {	/* env variable! */
	  while (isalnum(filename[i]))
	    varname[index++] = filename[i++];
	  varname[index] = '\0';

	  strcpy(env_value, getenv(varname));

	  if (strlen(env_value) == 0) {
	    error1("Don't know what the value of $%s is!", varname);
	    return(0);
	  }

	  sprintf(buffer, "%s%s%s", env_value, 
		  (filename[i] != '/' && lastch(env_value) != '/')? "/" : "",
		  (char *) filename + i);
	  strcpy(filename, buffer);
	}
	  
	return(1);
}
END-OF-FILE

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

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

chmod 644 src/file.c

echo done

exit 0