[mod.sources] Msg Shar.part.6

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

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

# Msg Shar part 6 of 7

# Shell Archive created by hpcnou!dat at Wed Feb 26 15:56:49 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/newmbox.c    src/strings.c       src/syscall.c       src/utils.c
#  src/validname.c  src/Makefile        utils/answer.c      utils/arepdaemon.c
#  utils/autoreply.c


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

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


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

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

/**  read new mbox file 

     (C) Copyright 1986 Dave Taylor
**/

#include <ctype.h>

#ifdef BSD
#undef tolower
#endif

#include "headers.h"

#include <sys/types.h>
#include <sys/stat.h>
#ifdef BSD
#  include <sys/time.h>
#else
#  include <time.h>
#endif

int
newmbox(stat, resync)
int stat, resync;
{
	/** Read a new mailbox file or resync on current file.

	    Values of stat and what they mean;

		stat = 0	- changing mailboxes from within program
		stat = 1	- read default mailbox for the first time
	        stat = 2	- read existing mailbox, new mail arrived

	    resync is TRUE iff we know the current mailbox has changed.  If
	    it's set to true this means that we MUST READ SOMETHING, even 
	    if it's the current mailbox again!!
	**/

	int  switching_to_default = 0;
	char buff[SLEN];

	dprint2("newmbox(stat=%d, resync=%d)\n", stat, resync);

	dprint1("\tfile-changed = %s\n\n", onoff(file_changed));

	if (stat > 0) {
	  if (strlen(infile) == 0)	/* no filename yet?? */
	    sprintf(infile,"%s%s",mailhome, username);
	}
	else { 		 	/* get name of new mailbox! */
	  MoveCursor(LINES-3, 30);
	  CleartoEOS();
	  show_last_error();
	  if (mbox_specified != 2) {
	    PutLine(LINES-2,0,"Name of new mailbox: ");
	    buff[0] = '\0';
	    (void) optionally_enter(buff, LINES-2, 21, FALSE);
	    ClearLine(LINES-2);
	    if (strlen(buff) == 0) {
	      if (resync && file_changed)
	        strcpy(buff, infile);
	      else
	        return(FALSE);
	    }
	    if (strcmp(buff, infile) == 0 && ! resync) {
	      error("already reading that mailbox!");
	      return(FALSE);
	    }
	    if (first_word(buff, mailhome) && ! resync) {
	      mbox_specified = 0; 	  /* fake program to think that */
	      stat = 1;		    	  /*     we're the default file */
	      switching_to_default++;	  /*        remember this act!  */
	    }
	    else if (strcmp(buff, "!") == 0) {	/* go to mailbox */
	      sprintf(buff,"%s%s", mailhome, username);
	      if (! resync || (resync && ! file_changed)) {
	        if (strcmp(buff, infile) == 0) {	/* are we reading it? */
	          error("already reading your incoming mailbox!");
		  return(FALSE);
	        }
	      }
	      if (! resync || (resync && ! file_changed)) {
	        mbox_specified = 0; 	/* fake program to think that */
	        stat = 1;	  	/*     we're the default file */
	        switching_to_default++;	/*        remember this act!  */
	      }
	    }

	    if (! expand_filename(buff)) {
	      error1("cannot expand file %s", buff);
	      if (resync && file_changed)
	        strcpy(buff, infile);
	      else
	        return(FALSE);	
	    }

	    if (! can_access(buff,"r")) {
	      error1("cannot open file %s", buff);
	      if (resync && file_changed)
	        strcpy(buff, infile);
	      else
	        return(FALSE);	
	    }

	    if (resync && file_changed && strcmp(buff, infile) == 0)
	      PutLine(20,40,"Resynchronizing file");
	    else
	      PutLine(20,40,"Mailbox: %s", buff);
	    CleartoEOLN();
	    strcpy(infile,buff);
	    if (! switching_to_default) mbox_specified = 1;
	  }
	  else { /* starting filename given to routine */
	    if (! can_access(infile,"r")) 
	      exit(PutLine(LINES, 0, "Could not open file %s!\n", infile));
	    mbox_specified = 1;
	  }
	}

	clear_error();
	clear_central_message();

	if ((mailfile = fopen(infile,"r")) == NULL) 
	  message_count = 0;
	else if (stat < 2) {          /* new mail file! */
	  if (notesfile)
	    message_count = read_notesfile();	/* can't get new notes! */
	  else
	    message_count = read_headers(FALSE);
	}
	else 	/* resync with current mail file */
	  message_count = read_headers(TRUE);

	if (message_count && stat < 2) current = 1;

	header_page = 0;

	return(TRUE);
}

int
read_headers(stat)
int stat;
{
	/** Reads the headers into the header_table structure and leaves
	    the file rewound for further I/O requests.   If the file being
	    read is the default mailbox (ie incoming) then it is copied to
	    a temp file and closed, to allow more mail to arrive during 
	    the msg session.  If stat is set, the program will then copy
	    the delete flag from the previous data structure value to the
	    new one if possible.  This is for re-reading the mailfile!
	    Added: reads and formats the date from the first 'From' line
	**/

	FILE *temp;
	char buffer[LONG_STRING], temp_filename[SLEN];
	register int line = 0, count = 0, subj = 0, copyit = 0, in_header = 1;
	long bytes = 0L, line_bytes = 0L;
	static int first_read = 0; 
	int count_x, count_y = 17;

	dprint1("read_headers(stat=%d)\n", stat);

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

	if (mbox_specified == 0) {
	  lock(INCOMING);	/* ensure no mail arrives while we do this! */
	  sprintf(temp_filename,"%s%s",temp_mbox, username);
	  if ((temp = fopen(temp_filename,"w")) == NULL) {
	    unlock();	/* remove lock file! */
	    Raw(OFF);
	    printf("Could not open file %s for writing (uid=%d)...\n",
	           temp_filename, getuid());
	    system_call("/bin/ls -l /tmp/*", SH);
	    leave(error1("could not open file %s for writing!",
	          temp_filename));
	  }
	  get_mailtime();
	  copyit++;
	  chown(temp_filename, userid, getgid());
	}

	while (fgets(buffer, LONG_STRING, mailfile) != NULL) {
	  if (line == 0) { 	/* first line of file... */	

	    if (! mbox_specified) {
	      if (first_word(buffer, "Forward to ")) 
	        set_central_message("Mail being forwarded to %s", 
                   (char *) (buffer + 11));
	    }

	    /* Are we reading in a notesfile file without the flag
	       turned on??? */
	
	    if (first_word(buffer, NOTES_HEADER)) {	/* if so...  */
	      rewind(mailfile);
	      notesfile++;		    /* set da flag, boss-man */
	      return(read_notesfile()); 	/* hop over to notes */
	    }
	  }

	  if (copyit) fputs(buffer, temp);
	  line_bytes = (long) strlen(buffer); 
	  line++;
	  if (first_word(buffer,"From ")) {
	    if (real_from(buffer, &header_table[count])) {
	      header_table[count].offset = (long) bytes;
	      if (! stat || count > message_count) 
	        header_table[count].delete = 0;		/* clear flag! */
	      strcpy(header_table[count].subject, "");	/* clear subj  */
	      header_table[count++].lines = line;
	      header_table[count].priority = 0;
	      subj = 0;
	      in_header = 1;
	      PutLine(count_x, count_y, "%d", count);
	      if (count > 1)
	        header_table[count-2].lines = header_table[count-1].lines - 
					      header_table[count-2].lines;
	    }
	  }
	  else if (in_header) {
	    if (first_word(buffer,">From")) 
	      forwarded(buffer, &header_table[count-1]); /* return address */
	    else if (first_word(buffer,"Subject:") ||
		     first_word(buffer,"Subj:") ||
		     first_word(buffer,"Re:")) {
	      if (! subj++) {
	        remove_first_word(buffer);
	        strncpy(header_table[count-1].subject, buffer, SLEN);
	      }
	    }
	    else if (first_word(buffer,"From:"))
	      parse_arpa_from(buffer, header_table[count-1].from);
	    else if (first_word(buffer, "Date:")) 
	      parse_arpa_date(buffer, &header_table[count-1]);
	    else if (first_word(buffer, "Priority:"))
	      header_table[count-1].priority++;
	    else if (buffer[0] == LINE_FEED || buffer[0] == '\0') {
	      if (in_header) {
	        in_header = 0;	/* in body of message! */
	        fix_date(&header_table[count-1]);
	      }
	    }
	  }
	  bytes += (long) line_bytes;
	}

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

	if (mbox_specified == 0) {
	  unlock();	/* remove lock file! */
	  fclose(mailfile);
	  fclose(temp);
	  if ((mailfile = fopen(temp_filename,"r")) == NULL) {
	    MoveCursor(LINES,0);
	    Raw(OFF);
	    printf("Fatal error: could not reopen %s as temp mail file!\n",
	           temp_filename);
	    leave();
	  }
	}
	else 
          rewind(mailfile);

	return(count);
}
END-OF-FILE

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

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

chmod 644 src/newmbox.c

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


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

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

/** This file contains all the string oriented functions for the
    MSG Mailer, and lots of other generally useful string functions! 

    For BSD systems, this file also includes the function "tolower"
    to translate the given character from upper case to lower case.

    (C) Copyright 1985, Dave Taylor
**/

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

#ifdef BSD
#undef tolower
#undef toupper
#endif

/** forward declarations **/

char *format_long(), *strip_commas(), *tail_of_string(), *shift_lower(),
     *get_token(), *strip_parens(), *argv_zero();

#ifdef BSD

int
tolower(ch)
char ch;
{
	/** This should be a macro call, but if you use this as a macro
	    calls to 'tolower' where the argument is a function call will
	    cause the function to be called TWICE which is obviously the
	    wrong behaviour.  On the other hand, to just blindly translate
	    assuming the character is always uppercase can cause BIG
	    problems, so...
	**/

	return ( isupper(ch) ? ch - 'A' + 'a' : ch );
}

int
toupper(ch)
char ch;
{
	/** see comment for above routine - tolower() **/

	return ( islower(ch) ? ch - 'a' + 'A' : ch );
}

#endif

int 
in_string(buffer, pattern)
char *buffer, *pattern;
{
	/** Returns TRUE iff pattern occurs IN IT'S ENTIRETY in buffer. **/ 

	register int i = 0, j = 0;
	
	while (buffer[i] != '\0') {
	  while (buffer[i++] == pattern[j++]) 
	    if (pattern[j] == '\0') 
	      return(TRUE);
	  i = i - j + 1;
	  j = 0;
	}
	return(FALSE);
}

tail_of(from, buffer, header_line)
char *from, *buffer;
int   header_line;
{
	/** Return last two words of 'from'.  This is to allow
	    painless display of long return addresses as simply the
	    machine!username.  Alternatively, if the first three
	    characters of the 'from' address are 'To:' and 'header_line'
	    is TRUE, then return the buffer value prepended with 'To '. 
	**/

	/** Note: '!' delimits Usenet nodes, '@' delimits ARPA nodes,
	          ':' delimits CSNet & Bitnet nodes, and '%' delimits
		  multiple stage ARPA hops... **/

	register int loc, i = 0, cnt = 0;
	char     tempbuffer[SLEN];
	

	for (loc = strlen(from)-1; loc >= 0 && cnt < 2; loc--) {
	  if (from[loc] == '!' || from[loc] == '@' ||
	      from[loc] == ':') cnt++;
	  if (cnt < 2) buffer[i++] = from[loc];
	}

	buffer[i] = '\0';

	reverse(buffer);

	if ((strncmp(buffer,"To:", 3) == 0) && header_line)
	  buffer[2] = ' ';
	else if ((strncmp(from, "To:", 3) == 0) && header_line) {
	  sprintf(tempbuffer,"To %s", buffer); 
	  strcpy(buffer, tempbuffer);
	}
	else if (strncmp(buffer, "To:", 3) == 0) {
	  for (i=3; i < strlen(buffer); i++)
	    tempbuffer[i-3] = buffer[i];
	  tempbuffer[i-3] = '\0';
	  strcpy(buffer, tempbuffer);
	}
}

char *format_long(inbuff, init_len)
char *inbuff;
int   init_len;
{
	/** Return buffer with \n\t sequences added at each point
	    where it would be more than 80 chars long.  It only 
	    allows the breaks at legal points (ie white spaces).
	    init-len is the characters already on the first line...
	    Changed so that if this is called while mailing without
	    the overhead of "msg", it'll include "\r\n\t" instead.
	**/

	static char ret_buffer[VERY_LONG_STRING];
	register int index = 0, current_length = 0, depth=15, i;
	char     buffer[VERY_LONG_STRING];
	char     *word, *bufptr;

	strcpy(buffer, inbuff);

	bufptr = (char *) buffer;

	current_length = init_len + 2;	/* for luck */

	while ((word = get_token(bufptr," ", depth)) != NULL) {
	  if (strlen(word) + current_length > 80) {
	    if (index > 0) {
	      if (mail_only)
	        ret_buffer[index++] = '\r';
	      ret_buffer[index++] = '\n';
	      ret_buffer[index++] = '\t';
	    }
	    for (i=0; i<strlen(word); i++)
	      ret_buffer[index++] = word[i];
	    current_length = strlen(word) + 8;	/* 8 = TAB */
	  }
	  else {
	    if (index > 0)
	      ret_buffer[index++] = ' ';
	    for (i=0; i<strlen(word); i++)
	      ret_buffer[index++] = word[i];
	    current_length += strlen(word) + 1;
	  }
	
	  bufptr = NULL;
	}
	
	ret_buffer[index] = '\0';

	return( (char *) ret_buffer);
}

char *strip_commas(string)
char *string;
{
	/** return string with all commas changed to spaces.  This IS
	    destructive and will permanently change the input string.. **/

	register int i;

	for (i=0; i < strlen(string); i++)
	  if (string[i] == COMMA)
	    string[i] = SPACE;

	return( (char *) string);
}

char *strip_parens(string)
char *string;
{
	/** Return string with all parenthesized information removed.
	    This is a non-destructive algorithm... **/

	static char  buffer[VERY_LONG_STRING];
	register int i, depth = 0, buffer_index = 0;

	for (i=0; i < strlen(string); i++) {
	  if (string[i] == '(')
	    depth++;
	  else if (string[i] == ')') 
	    depth--;
	  else if (depth == 0)
	    buffer[buffer_index++] = string[i];
	}
	
	buffer[buffer_index] = '\0';

	return( (char *) buffer);
}

move_left(string, chars)
char string[];
int  chars;
{
	/** moves string chars characters to the left DESTRUCTIVELY **/

	register int i;

	chars--; /* index starting at zero! */

	for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
	  string[i-chars] = string[i];

	string[i-chars] = '\0';
}

remove_first_word(string)
char *string;
{	/** removes first word of string, ie up to first non-white space
	    following a white space! **/

	register int loc;

	for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
	    ;

	while (string[loc] == ' ' || string[loc] == '\t')
	  loc++;
	
	move_left(string, ++loc);
}

char *tail_of_string(string, maxchars)
char *string;
int  maxchars;
{
	/** Return a string that is the last 'maxchars' characters of the
	    given string.  This is only used if the first word of the string
	    is longer than maxchars, else it will return what is given to
	    it... 
	**/

	static char buffer[SLEN];
	register int index, i;

	for (index=0;! whitespace(string[index]) && index < strlen(string); 
	     index++)
	  ;

	if (index < maxchars) {
	  strncpy(buffer, string, maxchars-2);	/* word too short */
	  buffer[maxchars-2] = '.';
	  buffer[maxchars-1] = '.';
	  buffer[maxchars]   = '.';
	  buffer[maxchars+1] = '\0';
	} 
	else {
	  i = maxchars;
	  buffer[i--] = '\0';
	  while (i > 1) 
	    buffer[i--] = string[index--];
	  buffer[2] = '.';
	  buffer[1] = '.';
	  buffer[0] = '.';
	}

	return( (char *) buffer);
}

reverse(string)
char *string;
{
	/** reverse string... pretty trivial routine, actually! **/

	char buffer[SLEN];
	register int i, j = 0;

	for (i = strlen(string)-1; i >= 0; i--)
	  buffer[j++] = string[i];

	buffer[j] = '\0';

	strcpy(string, buffer);
}

int
get_word(buffer, start, word)
char *buffer, *word;
int start;
{
	/**	return next word in buffer, starting at 'start'.
		delimiter is space or end-of-line.  Returns the
		location of the next word, or -1 if returning
		the last word in the buffer.  -2 indicates empty
		buffer!  **/

	register int loc = 0;

	while (buffer[start] == ' ' && buffer[start] != '\0')
	  start++;

	if (buffer[start] == '\0') return(-2);	 /* nothing IN buffer! */

	while (buffer[start] != ' ' && buffer[start] != '\0')
	  word[loc++] = buffer[start++];

	word[loc] = '\0';
	return(start);
}

int
chloc(string, ch)
char *string, ch;
{
	/** returns the index of ch in string, or -1 if not in string **/
	register int i;

	for (i=0; i<strlen(string); i++)
	  if (string[i] == ch) return(i);
	return(-1);
}

int
two_words(string)
char *string;
{
	/** is 'string' exactly two words?  Return TRUE or FALSE **/

	return( words_in_string(string) == 2 );

}

char *shift_lower(string)
char *string;
{
	/** return 'string' shifted to lower case.  Do NOT touch the
	    actual string handed to us! **/

	static char buffer[LONG_SLEN];
	register int i;

	for (i=0; i < strlen(string); i++)
	  if (isupper(string[i]))
	    buffer[i] = tolower(string[i]);
	  else
	    buffer[i] = string[i];
	
	buffer[strlen(string)] = 0;
	
	return( (char *) buffer);
}

int
words_in_string(buffer)
char *buffer;
{
	/** This routine returns the number of words in the given line.
	    A word is defined as a series of characters surrounded by
	    either the beginning of the string, the end of the string,
	    or white space.
		For example, the following line has 8 words:
	    "This is a test of the program, okay?"
	**/

	register int count = 0, i = 0;
	
	while (buffer[i] != '\0') {
	  
	  while (whitespace(buffer[i])) i++;
	
	  if (buffer[i] != '\0')
  	    count++;

	  while (! whitespace(buffer[i]) && buffer[i] != '\0') i++;
	}
	return(count);
}

clean_up(buffer)
char *buffer;
{
	/** This routine takes a string of the form "a:b" and returns it 
            as just "b"... **/

	char mybuffer[SLEN];
	register int loc, myloc = 0;

	for (loc=0; buffer[loc] != ':'; loc++)
	   ;

	while (buffer[++loc] != '\0')
	  mybuffer[myloc++] = buffer[loc];

	mybuffer[myloc] = '\0';

	strcpy(buffer, mybuffer);
}

Centerline(line, string)
int line;
char *string;
{
	/** Output 'string' on the given line, centered. **/

	register int length, col;

	length = strlen(string);

	if (length > COLUMNS)
	  col = 0;
	else
	  col = (COLUMNS - length) / 2;

	PutLine(line, col, string);
}

char *argv_zero(string)
char *string;
{
	/** given a string of the form "/something/name" return a
	    string of the form "name"... **/

	static char buffer[NLEN];
	register int i, j=0;

	for (i=strlen(string)-1; string[i] != '/'; i--)
	  buffer[j++] = string[i];
	buffer[j] = '\0';

	reverse(buffer);

	return( (char *) buffer);
}

#define MAX_RECURSION		20		/* up to 20 deep recursion */

char *get_token(source, keys, depth)
char *source, *keys;
int   depth;
{
	/** This function is similar to strtok() (see "opt_utils")
	    but allows nesting of calls via pointers... 
	**/

	register int  last_ch;
	static   char *buffers[MAX_RECURSION];
	char     *return_value, *sourceptr;

	if (depth > MAX_RECURSION) {
	   error1("get_token calls nested greater than %d deep!", 
		  MAX_RECURSION);
	   emergency_exit();
	}

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

	sourceptr += strspn(sourceptr, keys);	  /* skip the bad.. */
	
	if (*sourceptr == '\0') {
	  buffers[depth] = sourceptr;
	  return(NULL);			/* we've hit end-of-string   */
	}

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

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

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

	if (*sourceptr != '\0')		/** don't forget if we're at end! **/
	  sourceptr++;			      
	
	return_value[last_ch] = '\0';	      /* ..ending right      */

	buffers[depth] = sourceptr;	      /* save this, mate!    */

	return((char *) return_value);	     /* and we're outta here! */
}
END-OF-FILE

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

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

chmod 644 src/strings.c

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


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

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

	PutLine(LINES-3,COLUMNS-40,"(use 'sh' or 'csh' for a shell)");
	PutLine(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);

	printf("\nPress <return> to return to MSG: ");
	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("system_call('%s', %s)\n", string, 
	         shell_type == SH? "SH" : "USER-SHELL");

	if ((pid = fork()) == 0) {
	  setuid(userid);	/* back to the normal user! */
	  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
pipe()
{
	/** pipe the current message to the specified sequence.. **/

	char command[SLEN], buffer[LONG_SLEN];
	int  ret, lines;

	PutLine(LINES-2,0,"Pipe current msg to: ");
	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);

	lines = header_table[current-1].lines;

	if (cursor_control)  transmit_functions(OFF);
	
	sprintf(buffer, "%s %s %d %d - | %s", cutfile, 
		infile, header_table[current-1].offset,
		lines, command);

	ret = system_call(buffer, USER_SHELL);

	printf("\nPress <return> to return to MSG: ");
	Raw(ON);
	(void) getchar();
	if (cursor_control)  transmit_functions(ON);

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

printmsg()
{
	/** print specified message using 'printout' variable.  
	    Error message iff printout not defined! **/

	FILE *temp;
	char buffer[LONG_SLEN], filename[SLEN], printbuffer[LONG_SLEN];
	int  retcode;

	dprint1("printmsg()\n\tprintout = %s\n", printout);
	dprint1("\tcurrent message = %d\n", current);

	if (strlen(printout) == 0) {
	  error("PRINTMAIL not defined!  Don't know how to print message");
	  return;
	}
	
	if (current == 0) {
	  error("No mail to print!");
	  return;
	}

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

	if ((temp = fopen(filename,"w")) == NULL) {
	  error1("Could not open file %s as a temporary file", filename);
	  return;
	}

	copy_message("", temp, FALSE);

	fclose(temp);

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

        sprintf(buffer,"(%s 2>&1 ) > /dev/null",
		printbuffer, filename);

  	error("working...");

	if ((retcode = system_call(buffer, SH)) == 0)
	  error("Message queued up to print");
	else
	  error1("Printout failed with return code %d", retcode);

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

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

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

chmod 644 src/syscall.c

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


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

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

/** Utility routines for MSG 

    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>
#include <errno.h>

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

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

	if (cursor_control)
	  MoveCursor(LINES, 0);

	printf("\n\rEmergency Exit taken!  All temp files intact!\n\r\n\r");

	exit(1);
}

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

	dprint1("leave(%d)\n", val);

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

	if (! mail_only) {
	  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.lock",mailhome, username); /* lock file */
	(void) unlink(buffer);

	if (! mail_only)
	  MoveCursor(LINES-1,0);
	putchar('\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];

	dprint1("leave_locked(%d)\n", val);

	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-1,0);
	putchar('\n');

	exit(0);
}

int
get_page(current)
int current;
{
	/** 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 (current > last_on_page) {
	  header_page = (int) (current-1) / headers_per_page;
	  return(1);
	}
	else if (current < first_on_page) {
	  header_page = (int) (current-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;
	
	dprint1("copy_to_self(%s)\n", buffer);
	
	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

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

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

chmod 644 src/utils.c

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


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

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

	char filebuf[SLEN];

#ifdef NOCHECK_VALIDNAME

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

#else

	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

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

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

chmod 644 src/validname.c

# ---------- file src/Makefile ----------


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

echo extracting file src/Makefile...
cat << 'END-OF-FILE' > src/Makefile
#
#  Makefile for the MSG mail program.
#
#         (C) Copyright 1986, Dave Taylor
#
#  Last modification: January 23rd, 1986

CFILES=	addr_utils.c alias.c aliasdb.c aliaslib.c args.c curses.c date.c   \
	delete.c encode.c file.c file_utils.c fileio.c hdrconfg.c help.c   \
	initialize.c input_utils.c mailout.c mailtime.c mkhdrs.c msg.c     \
	newmbox.c notesfile.c output_utils.c pattern.c quit.c savecopy.c   \
	read_rc.c reply.c return_addr.c screen.c showmsg.c strings.c       \
	syscall.c utils.c validname.c softkeys.c opt_utils.c leavembox.c

HEADERS=../hdrs/curses.h ../hdrs/defs.h ../hdrs/headers.h ../hdrs/msg.h

OBJS=	addr_utils.o alias.o aliasdb.o aliaslib.o args.o curses.o date.o   \
	delete.o encode.o file.o file_utils.o fileio.o hdrconfg.o help.o   \
	initialize.o input_utils.o mailout.o mailtime.o mkhdrs.o msg.o     \
	newmbox.o notesfile.o output_utils.o pattern.o quit.o savecopy.o   \
	read_rc.o reply.o return_addr.o screen.o showmsg.o strings.o       \
	syscall.o utils.o validname.o softkeys.o opt_utils.o leavembox.o

# if on BSD use
# DEFINE=-DBSD
# else if on UTS use
# DEFINE=-DUTS
# else
DEFINE=

BIN=    ../bin
LIBS=   -ltermcap
CFLAGS= -O -I../hdrs
CC=	/bin/cc
RM=	/bin/rm -f

.c.o:   ${HEADERS}
	${CC} -c ${CFLAGS} ${DEFINE} $*.c 

../bin/msg: ${OBJS} ${EXTRA} ${HEADERS} ../hdrs/msg.h
	${CC} -o ${BIN}/msg -n ${OBJS} ${LIBS}

curses.o: curses.c ../hdrs/curses.h
	${CC} -c -O -DRAWMODE ${DEFINE} -I../hdrs curses.c

clean:
	${RM} ${OBJS} LINT.OUT

lint: LINT.OUT

LINT.OUT: ${CFILES}
	lint -p -I../hdrs ${CFILES} > LINT.OUT

listing:
	@../bin/makelisting Makefile ${HEADERS} ${CFILES}
	@echo LISTING generated.
END-OF-FILE

size=`wc -c < src/Makefile`

if [ $size != 1647 ]
then
  echo Warning: src/Makefile changed - should be 1647 bytes, not $size bytes
fi

chmod 644 src/Makefile

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

# ---------- file utils/answer.c ----------


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

echo extracting file utils/answer.c...
cat << 'END-OF-FILE' > utils/answer.c
/**			answer.c			**/

/** This program is a phone message transcription system, and
    is designed for secretaries and the like, to allow them to
    painlessly generate electronic mail instead of paper forms.

    Note: this program ONLY uses the local alias file, and does not
	  even read in the system alias file at all.

    (C) Copyright 1986, Dave Taylor

**/

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

#include "defs.h"	/* MSG system definitions  */

struct alias_rec user_hash_table  [MAX_UALIASES];

int user_data;		/* fileno of user data file   */

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

main()
{
	FILE *fd;
	char *address, buffer[LONG_STRING], tempfile[SLEN];
	char  name[SLEN], user_name[SLEN];
	int   msgnum = 0, eof;
	
	read_alias_files();

	while (1) {
	  if (msgnum > 9999) msgnum = 0;
	
	  printf("\n-------------------------------------------------------------------------------\n");

prompt:   printf("\nMessage to: ");
	  gets(user_name, SLEN);
	  
	  if ((strcmp(user_name,"quit") == 0) ||
	      (strcmp(user_name,"exit") == 0) ||
	      (strcmp(user_name,"done") == 0) ||
	      (strcmp(user_name,"bye")  == 0))
	     exit(0);

	  if (translate(user_name, name) == 0)
	    goto prompt;

	  address = get_alias_address(name, 1, 0);

	  if (strlen(address) == 0) {
	    printf("Sorry, could not find '%s' [%s] in list!\n", user_name, 
		   name);
	    goto prompt;
	  }

	  sprintf(tempfile, "%s%d", temp_file, msgnum++);

	  if ((fd = fopen(tempfile,"w")) == NULL)
	    exit(printf("** Fatal Error: could not open %s to write\n",
		 tempfile));


	  printf("\nEnter message for %s ending with a blank line.\n\n", 
		 user_name);

	  fprintf(fd,"\n\n");

	  do {
	   printf("> ");
	   if (! (eof = (gets(buffer, SLEN) == NULL))) 
	     fprintf(fd, "%s\n", buffer);
	  } while (! eof && strlen(buffer) > 0);
	
	  fclose(fd);
 
	  sprintf(buffer, "(%s -s \"While You Were Out\" %s < %s ; %s %s) &", 
	          mailx, address, tempfile, remove, tempfile);

	  system(buffer);
	}
}

int
translate(fullname, name)
char *fullname, *name;
{
	/** translate fullname into name..
	       'first last'  translated to first_initial - underline - last
	       'initial last' translated to initial - underline - last
	    Return 0 if error.
	**/
	register int i, lastname = 0;

	for (i=0; i < strlen(fullname); i++) {

	  if (isupper(fullname[i]))
	     fullname[i] = fullname[i] - 'A' + 'a';

	  if (fullname[i] == ' ') 
	    if (lastname) {
	      printf(
	      "** Can't have more than 'FirstName LastName' as address!\n");
	      return(0);
	    }
	    else
	      lastname = i+1;
	
	}

	if (lastname) 
	  sprintf(name, "%c_%s", fullname[0], (char *) fullname + lastname);
	else
	  strcpy(name, fullname);

	return(1);
}

	    
read_alias_files()
{
	/** read the user alias file **/

	char fname[SLEN];
	int  hash;

	sprintf(fname,  "%s/.alias_hash", getenv("HOME")); 

	if ((hash = open(fname, O_RDONLY)) == -1) 
	  exit(printf("** Fatal Error: Could not open %s!\n", fname));

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

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

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

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.  Returns NULL if not found.
	    Depth is the nesting depth, and varies according to the
	    nesting level of the routine.  **/

	static char buffer[VERY_LONG_STRING];
	int    loc;

	if ((loc = find(name, user_hash_table, MAX_UALIASES)) >= 0) {
	  lseek(user_data, user_hash_table[loc].byte, 0L);
	  get_line(user_data, buffer, LONG_STRING);
	  if (buffer[0] == '!' && mailing)
	    return( (char *) expand_group(buffer, depth));
	  else
	    return( (char *) buffer);
	}
	
	return( (char *) NULL);
}

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 the current recursion
	    depth of the expansion (for the 'get_token' routine) **/

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

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

	while ((word = (char *) get_token(bufptr, "!, ", depth)) != NULL) {
	  if ((address = (char *) get_alias_address(word, 1, depth)) == NULL) {
	    fprintf(stderr, "Alias %s not found for group expansion!", word);
	    return( (char *) NULL);
	  }
	  else if (strcmp(buffer,address) != 0) {
	    sprintf(buffer,"%s %s", buffer, 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;
}

print_long(buffer, init_len)
char *buffer;
int   init_len;
{
	/** print buffer out, 80 characters (or less) per line, for
	    as many lines as needed.  If 'init_len' is specified, 
	    it is the length that the first line can be.
	**/

	register int i, loc=0, space, length; 

	/* In general, go to 80 characters beyond current character
	   being processed, and then work backwards until space found! */

	length = init_len;

	do {
	  if (strlen(buffer) > loc + length) {
	    space = loc + length;
	    while (buffer[space] != ' ' && space > loc + 50) space--;
	    for (i=loc;i <= space;i++)
	      putchar(buffer[i]);
	    putchar('\n');
	    loc = space;
	  }
	  else {
	    for (i=loc;i < strlen(buffer);i++)
	      putchar(buffer[i]);
	    putchar('\n');
	    loc = strlen(buffer);
	  }
	  length = 80;
	} while (loc < strlen(buffer));
}

/****
     The following is a newly chopped version of the 'strtok' routine
  that can work in a recursive way (up to 20 levels of recursion) by
  changing the character buffer to an array of character buffers....
****/

#define MAX_RECURSION		20		/* up to 20 deep recursion */

#undef  NULL
#define NULL			(char *) 0	/* for this routine only   */

extern int strspn();
extern char *strpbrk();

char *get_token(string, sepset, depth)
char *string, *sepset;
int  depth;
{

	/** string is the string pointer to break up, sepstr are the
	    list of characters that can break the line up and depth
	    is the current nesting/recursion depth of the call **/

	register char	*p, *q, *r;
	static char	*savept[MAX_RECURSION];

	/** is there space on the recursion stack? **/

	if (depth >= MAX_RECURSION) {
	 fprintf(stderr,"Error: Get_token calls nested greated than %d deep!\n",
			MAX_RECURSION);
	 exit(1);
	}

	/* set up the pointer for the first or subsequent call */
	p = (string == NULL)? savept[depth]: string;

	if(p == 0)		/* return if no tokens remaining */
		return(NULL);

	q = p + strspn(p, sepset);	/* skip leading separators */

	if (*q == '\0')		/* return if no tokens remaining */
		return(NULL);

	if ((r = strpbrk(q, sepset)) == NULL)	/* move past token */
		savept[depth] = 0;	/* indicate this is last token */
	else {
		*r = '\0';
		savept[depth] = ++r;
	}
	return(q);
}
END-OF-FILE

size=`wc -c < utils/answer.c`

if [ $size != 8207 ]
then
  echo Warning: utils/answer.c changed - should be 8207 bytes, not $size bytes
fi

chmod 644 utils/answer.c

# ---------- file utils/arepdaemon.c ----------


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

echo extracting file utils/arepdaemon.c...
cat << 'END-OF-FILE' > utils/arepdaemon.c
/**			arepdaemon.c			**/

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

/** Keep track of mail as it arrives, and respond by sending a 'recording'
    file to the sender as new mail is received.

    Note: the user program that interacts with this program is the
    'autoreply' program and that should be consulted for further
    usage information.

    This program is part of the 'autoreply' system, and is designed
    to run every hour and check all mailboxes listed in the file 
    "/etc/autoreply.data", where the data is in the form:

	username	replyfile	current-mailfile-size

    To avoid a flood of autoreplies, this program will NOT reply to mail 
    that contains header "X-Mailer: fastmail".  Further, each time the 
    program responds to mail, the 'mailfile size' entry is updated in
    the file /etc/autoreply.data to allow the system to be brought 
    down and rebooted without any loss of data or duplicate messages.

    This daemon also uses a lock semaphore file, /usr/spool/uucp/LCK..arep,
    to ensure that more than one copy of itself is never running.  For this
    reason, it is recommended that this daemon be started up each morning
    from cron, since it will either start since it's needed or simply see
    that the file is there and disappear.

    Since this particular program is the main daemon answering any
    number of different users, it must be run with uid root.

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

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

#include "defs.h"

#define arep_lock_file	"/usr/spool/uucp/LCK..arep"

#define autoreply_file	"/etc/autoreply.data"
#define fastmail	"/usr/local/bin/fastmail"

#define logfile		"/etc/autoreply.log"	/* first choice   */
#define logfile2	"/tmp/autoreply.log"	/* second choice  */

#define BEGINNING	0		/* see fseek(3S) for info */
#define SLEEP_TIME	3600		/* run once an hour       */
#define MAX_PEOPLE	20		/* max number in program  */

#define EXISTS		00		/* lock file exists??     */
#define MODE		0777		/* lockfile creation mode */

#define NLEN		20

#define remove_return(s)	if (strlen(s) > 0) { \
			          if (s[strlen(s)-1] == '\n') \
				    s[strlen(s)-1] = '\0'; \
		                }

struct replyrec {
	char 	username[NLEN];		/* login name of user */
	char	mailfile[SLEN];		/* name of mail file  */
	char    replyfile[SLEN];	/* name of reply file */
	long    mailsize;		/* mail file size     */
	int     in_list;		/* for new replies    */
      } reply_table[MAX_PEOPLE];

FILE  *logfd;				/* logfile (log action)   */
long  autoreply_size = 0L;		/* size of autoreply file */
int   active = 0;			/* # of people 'enrolled' */

FILE  *open_logfile();			/* forward declaration    */

long  bytes();				/*       ditto 		  */

main()
{
	long size;
	int  person, data_changed;

	if (! lock())
	  exit(0);	/* already running! */

	while (1) {

	  logfd = open_logfile();	/* open the log */

	  /* 1. check to see if autoreply table has changed.. */

	  if ((size = bytes(autoreply_file)) != autoreply_size) {
	    read_autoreply_file(); 
	    autoreply_size = size;
	  }

	  /* 2. now for each active person... */
	
	  data_changed = 0;

	  for (person = 0; person < active; person++) {
	    if ((size = bytes(reply_table[person].mailfile)) != 
		reply_table[person].mailsize) {
	      if (size > reply_table[person].mailsize)
	        read_newmail(person);
	      /* else mail removed - resync */
	      reply_table[person].mailsize = size;
	      data_changed++;
	    }
	  }

	  /* 3. if data changed, update autoreply file */

	  if (data_changed)
	    update_autoreply_file();

	  close_logfile();		/* close the logfile again */

	  /* 4. Go to sleep...  */

	  sleep(SLEEP_TIME);
	}
}

int
read_autoreply_file()
{
	/** We're here because the autoreply file has changed size!!  It
	    could either be because someone has been added or because
	    someone has been removed...since the list will always be in
	    order (nice, eh?) we should have a pretty easy time of it...
	**/

	FILE *file;
	char username[SLEN], 	replyfile[SLEN];
	int  person;
 	long size;
	
	log("Autoreply data file has changed!  Reading...");

	if ((file = fopen(autoreply_file,"r")) == NULL) {
	  log("No-one is using autoreply...");
	  return(0);
	}
	
	for (person = 0; person < active; person++)
	  reply_table[person].in_list = 0;
	
	while (fscanf(file, "%s %s %dl", username, replyfile, &size) != EOF) {
	  /* check to see if this person is already in the list */
	  if ((person = in_list(username)) != -1) {
	    reply_table[person].in_list = 1;
	    reply_table[person].mailsize = size;	 /* sync */
	  }
	  else { 	/* if not, add them */
	    if (active == MAX_PEOPLE) {
	      unlock();
	      exit(log("Couldn't add %s - already at max people!", 
		         username));
	    }
	    log("adding %s to the active list", username);
	    strcpy(reply_table[active].username, username);
	    sprintf(reply_table[active].mailfile, "/usr/mail/%s", username);
	    strcpy(reply_table[active].replyfile, replyfile);
	    reply_table[active].mailsize = size;
	    reply_table[active].in_list = 1;	/* obviously! */
	    active++;
	  }
	}

	/** now check to see if anyone has been removed... **/

	for (person = 0; person < active; person++)
	  if (reply_table[person].in_list == 0) {
	     log("removing %s from the active list", 
		  reply_table[person].username);
	    strcpy(reply_table[person].username, 
		   reply_table[active-1].username);
	    strcpy(reply_table[person].mailfile, 
		   reply_table[active-1].mailfile);
	    strcpy(reply_table[person].replyfile, 
		   reply_table[active-1].replyfile);
	    reply_table[person].mailsize = reply_table[active-1].mailsize;
	    active--;
	  }
}

update_autoreply_file()
{
	/** update the entries in the autoreply file... **/

	FILE *file;
	register int person;

	if ((file = fopen(autoreply_file,"w")) == NULL) {
          log("Couldn't update autoreply file!");
	  return;
	}

	for (person = 0; person < active; person++)
	  fprintf(file, "%s %s %ld\n",
		  reply_table[person].username,
		  reply_table[person].replyfile,
		  reply_table[person].mailsize);

	fclose(file);

	printf("updated autoreply file\n");
	autoreply_size = bytes(autoreply_file);
}

int
in_list(name)
char *name;
{
	/** search the current active reply list for the specified username.
	    return the index if found, or '-1' if not. **/

	register int index;

	for (index = 0; index < active; index++)
	  if (strcmp(name, reply_table[index].username) == 0)
	    return(index);
	
	return(-1);
}

read_newmail(person)
int person;
{
	/** Read the new mail for the specified person. **/

	
	FILE *mailfile;
	char from_whom[LONG_SLEN], subject[SLEN];
	int  sendit;

	log("New mail for %s", reply_table[person].username);

        if ((mailfile = fopen(reply_table[person].mailfile,"r")) == NULL)
           return(log("can't open mailfile for user %s", 
		    reply_table[person].username));

        if (fseek(mailfile, reply_table[person].mailsize, BEGINNING) == -1)
           return(log("couldn't seek to %ld in mail file!", 
	               reply_table[person].mailsize));

	while (get_return(mailfile, person, from_whom, subject, &sendit) != -1)
	  if (sendit)
	    reply_to_mail(person, from_whom, subject);

	return;
}

int
get_return(file, person, from, subject, sendit)
FILE *file;
int  person, *sendit;
char *from, *subject;
{
	/** Reads the new message and return the from and subject lines.
	    sendit is set to true iff it isn't a machine generated msg
	**/
	
    char name1[SLEN], name2[SLEN], lastname[SLEN];
    char buffer[LONG_SLEN], hold_return[NLEN];
    int done = 0, in_header = 0;

    from[0] = '\0';
    *sendit = 1;

    while (! done) {

      if (fgets(buffer, LONG_SLEN, file) == NULL)
	return(-1);

      if (first_word(buffer, "From ")) {
	in_header++;
	sscanf(buffer, "%*s %s", hold_return);
      }
      else if (in_header) {
        if (first_word(buffer, ">From")) {
	  sscanf(buffer,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s", name1, name2);
	  add_site(from, name2, lastname);
        }
        else if (first_word(buffer,"Subject:")) {
	  remove_return(buffer);
	  strcpy(subject, (char *) (buffer + 8));
        }
        else if (first_word(buffer,"X-Mailer: fastmail"))
	  *sendit = 0;
        else if (strlen(buffer) == 1)
	  done = 1;
      }
    }

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

    return(0);
}

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

	char local_buffer[LONG_SLEN], *strip_parens();

	if (strcmp(site, "uucp") != 0)
	  if (strcmp(site, lastsite) != 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! */
	   }
}

remove_first_word(string)
char *string;
{	/** removes first word of string, ie up to first non-white space
	    following a white space! **/

	register int loc;

	for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
	    ;

	while (string[loc] == ' ' || string[loc] == '\t')
	  loc++;
	
	move_left(string, loc);
}

move_left(string, chars)
char string[];
int  chars;
{
	/** moves string chars characters to the left DESTRUCTIVELY **/

	register int i;

	chars--; /* index starting at zero! */

	for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
	  string[i-chars] = string[i];

	string[i-chars] = '\0';
}

reply_to_mail(person, from, subject)
int   person;
char *from, *subject;
{
	/** Respond to the message from the specified person with the
	    specified subject... **/
	
	char buffer[SLEN];

	if (strlen(subject) == 0)
	  strcpy(subject, "Auto-reply Mail");
	else if (! first_word(subject,"Auto-reply")) {
	  sprintf(buffer, "Auto-reply to:%s", subject);
	  strcpy(subject, buffer);
	}

	log("auto-replying to '%s'", from);

	mail(from, subject, reply_table[person].replyfile, person);
}	

reverse(string)
char *string;
{
	/** reverse string... pretty trivial routine, actually! **/

	char buffer[SLEN];
	register int i, j = 0;

	for (i = strlen(string)-1; i >= 0; i--)
	  buffer[j++] = string[i];

	buffer[j] = '\0';

	strcpy(string, buffer);
}

long
bytes(name)
char *name;
{
	/** return the number of bytes in the specified file.  This
	    is to check to see if new mail has arrived....  **/

	int ok = 1;
	extern int errno;	/* system error number! */
	struct stat buffer;

	if (stat(name, &buffer) != 0)
	  if (errno != 2) {
	   unlock();
	   exit(fprintf(stderr,"Error %d attempting fstat on %s", errno, name));
	  }
	  else
	    ok = 0;
	
	return(ok ? buffer.st_size : 0);
}

mail(to, subject, filename, person)
char *to, *subject, *filename;
int   person;
{
	/** Mail 'file' to the user from person... **/
	
	char buffer[VERY_LONG_STRING];

	sprintf(buffer, "%s -f '%s [autoreply]' -s '%s' %s %s",
		fastmail, reply_table[person].username,
	        subject, filename, to);
	
	system(buffer);
}

log(message, arg)
char *message;
char *arg;
{
	/** Put log entry into log file.  Use the format:
	      date-time: <message>
	**/

	struct tm *localtime(), *thetime;
	long      time(), clock;
	char      buffer[SLEN];

	/** first off, get the time and date **/

	clock = time((long *) 0);       /* seconds since ???   */
	thetime = localtime(&clock);	/* and NOW the time... */

	/** then put the message out! **/

	sprintf(buffer, message, arg);

	fprintf(logfd,"%d/%d-%d:%02d: %s\n", 
		thetime->tm_mon+1, thetime->tm_mday,
	        thetime->tm_hour,  thetime->tm_min,
	        buffer);
}

FILE *open_logfile()
{
	/** open the logfile.  returns a valid file descriptor **/

	FILE *fd;

	if ((fd = fopen(logfile, "a")) == 0)
	  if ((fd = fopen(logfile2, "a")) == 0) {
	    unlock();
	    exit(1);	/* give up! */
	  }

	return( (FILE *) fd);
}

close_logfile()
{
	/** Close the logfile until needed again. **/

	fclose(logfd);
}

char *strip_parens(string)
char *string;
{
	/** Return string with all parenthesized information removed.
	    This is a non-destructive algorithm... **/

	static char  buffer[LONG_SLEN];
	register int i, depth = 0, buffer_index = 0;

	for (i=0; i < strlen(string); i++) {
	  if (string[i] == '(')
	    depth++;
	  else if (string[i] == ')') 
	    depth--;
	  else if (depth == 0)
	    buffer[buffer_index++] = string[i];
	}
	
	buffer[buffer_index] = '\0';

	return( (char *) buffer);
}

/*** LOCK and UNLOCK - ensure only one copy of this daemon running at any
     given time by using a file existance semaphore (wonderful stuff!) ***/

lock()
{
	/** Try to create the lock file.  If it's there, or we can't
	    create it for some stupid reason, return zero, otherwise,
	    a non-zero return code indicates success in locking this
	    process in. **/

	if (access(arep_lock_file, EXISTS) == 0)
	  return(0); 	/* file already exists!! */

	if (creat(arep_lock_file, MODE) == -1)
	  return(0);	/* can't create file!!   */

	return(1);
}

unlock()
{
	/** remove lock file if it's there! **/

	(void) unlink(arep_lock_file);
}
END-OF-FILE

size=`wc -c < utils/arepdaemon.c`

if [ $size != 13340 ]
then
  echo Warning: utils/arepdaemon.c changed - should be 13340 bytes, not $size bytes
fi

chmod 644 utils/arepdaemon.c

# ---------- file utils/autoreply.c ----------


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

echo extracting file utils/autoreply.c...
cat << 'END-OF-FILE' > utils/autoreply.c
/**			autoreply.c			**/

/** This is the front-end for the autoreply system, and performs two 
    functions: it either adds the user to the list of people using the
    autoreply function (starting the daemon if no-one else) or removes
    a user from the list of people.

    Usage:  autoreply filename
	    autoreply "off"
	or  autoreply		[to find current status]
    
    (C) 1986, Dave Taylor
**/

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

#define  SLEN		80			/* a normal string      */
#define  NLEN		20			/* a short string       */

#define  READ_ACCESS	04   			/* is file readable?    */

#define  mailhome	"/usr/mail"		/* where new mail lives */
#define  tempdir	"/tmp/arep"		/* file prefix          */
#define  autoreply_file	"/etc/autoreply.data"   /* autoreply data file  */

extern   int errno;				/* system error code    */
char     username[NLEN];			/* login name of user   */

main(argc, argv)
int    argc;
char *argv[];
{
	char filename[SLEN];

	if (argc > 2) {
	  printf("Usage: %s <filename>\tto start autoreply,\n", argv[0]);
	  printf("       %s off\t\tto turn off autoreply\n", argv[0]);
	  printf("   or  %s    \t\tto check current status\n", argv[0]);
	  exit(1);
	}

	(void) cuserid(username);

	if (strcmp(argv[1], "off") == 0 || argc == 1) 
	  remove_user((argc == 1));
	else {
	  strcpy(filename, argv[1]);
	  if (access(filename,READ_ACCESS) != 0) {
	    printf("Error: Can't read file '%s'\n", filename);
	    exit(1);
	  }
	  
	  if (filename[0] != '/') /* prefix home directory */
	    sprintf(filename,"%s/%s", getenv("HOME"), argv[1]);

	  add_user(filename);
	}

	exit(0);
}

remove_user(stat_only)
int stat_only;
{
	/** Remove the user from the list of currently active autoreply 
	    people.  If 'stat_only' is set, then just list the name of
	    the file being used to autoreply with, if any. **/

	FILE *temp, *repfile;
	char  tempfile[SLEN], user[SLEN], filename[SLEN];
	int   c, copied = 0, found = 0;
	long  filesize, bytes();

	if (! stat_only) {
	  sprintf(tempfile, "%s.%06d", tempdir, getpid());

	  if ((temp = fopen(tempfile, "w")) == NULL) {
	    printf("Error: couldn't open tempfile '%s'.  Not removed\n",
		    tempfile);
	    exit(1);
	  }
	}

	if ((repfile = fopen(autoreply_file, "r")) == NULL) {
	  if (stat_only) {
	    printf("You're not currently autoreplying to mail.\n");
	    exit(0);
	  }
	  printf("No-one is autoreplying to their mail!\n");
	  exit(0);
	}

	/** copy out of real replyfile... **/

	while (fscanf(repfile, "%s %s %ld", user, filename, &filesize) != EOF) 

	  if (strcmp(user, username) != 0) {
	    if (! stat_only) {
	      copied++;
	      fprintf(tempfile, "%s %s %ld\n", user, filename, filesize);
	    }
	  }
	  else {
	    if (stat_only) {
	      printf("You're currently autoreplying to mail with the file %s\n",		      filename); 
	      exit(0);
	    }
	    found++;
	  }

	fclose(temp);
	fclose(repfile);

	if (! found) {
	  printf("You're not currently autoreplying to mail%s\n",
		  stat_only? "." : "!");
	  if (! stat_only)
	    unlink(tempfile);
	  exit(! stat_only);
	}

	/** now copy tempfile back into replyfile **/

	if (copied == 0) {	/* removed the only person! */
	  unlink(autoreply_file);
	}
	else {			/* save everyone else   */
	  
	  if ((temp = fopen(tempfile,"r")) == NULL) {
	    printf("Error: couldn't reopen tempfile '%s'.  Not removed.\n",
		    tempfile);
	    unlink(tempfile);
	    exit(1);
	  }

	  if ((repfile = fopen(autoreply_file, "w")) == NULL) {
	    printf(
          "Error: couldn't reopen autoreply file for writing!  Not removed.\n");
	    unlink(tempfile);
	    exit(1);
	  }

	  while ((c = getc(temp)) != EOF)
	    putc(c, repfile);

	  fclose(temp);
	  fclose(repfile);
	
	}
	unlink(tempfile);

	if (found > 1)
	  printf("Warning: your username appeared %d times!!   Removed all\n", 
		  found);
	else
	  printf("You've been removed from the autoreply table.\n");
}

add_user(filename)
char *filename;
{
	/** add the user to the autoreply file... **/

	FILE *repfile;
	char  mailfile[SLEN];
	long  bytes();

	if ((repfile = fopen(autoreply_file, "a")) == NULL) {
	  printf("Error: couldn't open the autoreply file!  Not added\n");
	  exit(1);
	}
	
	sprintf(mailfile,"%s/%s", mailhome, username);

	fprintf(repfile,"%s %s %ld\n", username, filename, bytes(mailfile));

	fclose(repfile);

	printf("You've been added to the autoreply system.\n");
}


long
bytes(name)
char *name;
{
	/** return the number of bytes in the specified file.  This
	    is to check to see if new mail has arrived....  **/

	int ok = 1;
	extern int errno;	/* system error number! */
	struct stat buffer;

	if (stat(name, &buffer) != 0)
	  if (errno != 2)
	   exit(fprintf(stderr,"Error %d attempting fstat on %s", errno, name));
	  else
	    ok = 0;
	
	return(ok ? buffer.st_size : 0L);
}
END-OF-FILE

size=`wc -c < utils/autoreply.c`

if [ $size != 4840 ]
then
  echo Warning: utils/autoreply.c changed - should be 4840 bytes, not $size bytes
fi

chmod 644 utils/autoreply.c

echo done

exit 0