[comp.sources.unix] v18i103: Elm mail system, release 2.2, Part24/24

rsalz@uunet.uu.net (Rich Salz) (04/14/89)

Submitted-by: dsinc!syd@uunet.UU.NET (Syd Weinstein)
Posting-number: Volume 18, Issue 103
Archive-name: elm2.2/part24

#!/bin/sh
# this is part 24 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file utils/newalias.c continued
#
CurArch=24
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file utils/newalias.c"
sed 's/^X//' << 'SHAR_EOF' >> utils/newalias.c
X	  exit(1);
X	}
X
X	loc = hash_it(word, size);
X
X	while (table[loc].name[0] != '\0' && strcmp(table[loc].name, word) != 0)
X	  loc = (loc + 1) % size; 
X
X	if (table[loc].name[0] == '\0') {
X	  strcpy(table[loc].name, word);
X	  table[loc].byte = offset;
X	}
X	else 
X	  printf("** Duplicate alias '%s' in file.  Multiples ignored.\n",
X	         word);
X}
X
Xint
Xhash_it(string, table_size)
Xchar *string;
X{
X	/** compute the hash function of the string, returning
X	    it (mod table_size) **/
X
X	register int i, sum = 0;
X	
X	for (i=0; string[i] != '\0'; i++)
X	  sum += (int) string[i];
X
X	return(sum % table_size);
X}
X
Xinit_table(table, size)
Xstruct alias_rec table[];
Xint size;
X{
X	/** initialize hash table! **/
X
X	register int i;
X
X	for (i=0; i < size; i++)
X	  table[i].name[0] = '\0';
X}
X
Xread_in_system(table, size)
Xstruct alias_rec table[];
Xint size;
X{
X	/** read in the system hash table...to check for group aliases
X	    from the user alias file (to ensure that there are no names
X	    in the user group files that are not purely contained within
X	    either alias table) **/
X	
X	int  fd;
X	char fname[SLEN];
X
X	sprintf(fname, "%s/%s", mailhome, ALIAS_HASH);
X
X	if ((fd = open(fname, O_RDONLY)) == -1)
X	  return;	/* no sweat: flag 'hash_table_loaded' not set! */
X
X	(void) read(fd, table, size);
X	close(fd);
X	hash_table_loaded++;
X}
X	
Xcheck_group(names, groupname)
Xchar *names, *groupname;
X{
X	/** one by one make sure each name in the group is defined
X	    in either the system alias file or the user alias file.
X	    This search is linearly dependent, so all group aliases
X	    in the source file should appear LAST, after all the user
X	    aliases! **/
X
X	char *word, *bufptr, buffer[LONG_STRING];
X
X	strcpy(buffer, names);
X	bufptr = (char *) buffer;
X
X	while ((word = (char *) strtok(bufptr,", ")) != NULL) {
X	  if (! can_find(word)) 
X	    if (! valid_name(word)) {
X	      error++;
X	      printf("** Alias %s in group %s is bad!\n", word, groupname);
X	    }
X	  bufptr = NULL;
X	}
X}
X
Xint
Xcan_find(name)
Xchar *name;
X{	
X	/** find name in either hash table...use 'is_system' variable to
X	    determine if we should look in both or just system....    **/
X
X	register int loc;
X	
X	if (strlen(name) > 20) {
X	  error++;
X	  printf("** Bad alias name: %s.  Too long.\n", name);
X	  return(1);	/* fake out: don't want 2 error messages! */
X	}
X
X	/** system alias table... **/
X	if (hash_table_loaded || is_system) {
X	  loc = hash_it(name, MAX_SALIASES);
X
X	  while (strcmp(name, shash_table[loc].name) != 0 && 
X                 shash_table[loc].name[0] != '\0')
X	    loc = (loc + 1) % MAX_SALIASES; 
X  
X	  if (strcmp(name, shash_table[loc].name) == 0)
X	    return(1);	/* found it! */
X	}
X
X	if (! is_system) {	/* okay! Let's check the user alias file! */
X	  loc = hash_it(name, MAX_UALIASES);
X
X	  while (strcmp(name, uhash_table[loc].name) != 0 && 
X                 uhash_table[loc].name[0] != '\0')
X	    loc = (loc + 1) % MAX_UALIASES; 
X
X	  if (strcmp(name, uhash_table[loc].name) == 0)
X	    return(1);	/* found it! */
X	}
X
X	return(0);
X}
X
Xextract_comment(comment, buffer, first, last)
Xchar *comment, *buffer;
Xint first, last;
X{
X	/** Buffer contains a comment, located between the first and last
X	    values.  Copy that into 'comment', but remove leading and
X	    trailing white space.  Note also that it doesn't copy past
X	    a comma, so `unpublishable' comments can be of the form;
X		dave: Dave Taylor, HP Labs : taylor@hplabs
X	    and the output will be "taylor@hplabs (Dave Taylor)".
X	**/
X
X	register int loc = 0; 
X
X	/** first off, skip the LEADING white space... **/
X
X	while (whitespace(buffer[first])) first++;
X	
X	/** now let's backup the 'last' value until we hit a non-whitespace **/
X
X	last -= 2;	/* starts at ch AFTER equals.. */
X	while (whitespace(buffer[last])) last--;
X
X	/** now a final check to make sure we're still talking about a 
X	    reasonable string (rather than a "joe :: joe@dec" type string) **/
X
X	if (first < last) {
X	  /* one more check - let's find the comma, if present... */
X	  for (loc=first; loc < last; loc++)
X	    if (buffer[loc] == ',') {
X	      last = loc-1;
X	      break;
X	  }
X	  loc = 0;
X	  while (first <= last)
X	    comment[loc++] = buffer[first++];
X	  comment[loc] = '\0';
X	}
X}
X
Xupdate_alias_file_locations()
X{
X	/** a short-term routine to ensure that the data files are
X	    moved into the correct directory... **/
X
X	char source[SLEN], dest[SLEN];
X
X	/** first let's create the directory if it ain't there... **/
X
X	sprintf(source, "%s/.elm", home);
X
X	/** Some systems don't have a mkdir call - how inconvienient! **/
X#ifdef MKDIR
X	(void) mkdir(source, 0700);
X#else
X	system("mkdir $HOME/.elm");
X	system("chmod 700 $HOME/.elm");
X#endif /* MKDIR */
X
X	/** now *link* the files... **/
X
X	sprintf(source, "%s/.alias_text", home);
X	sprintf(dest,   "%s/%s",          home, ALIAS_TEXT);
X	link(source, dest);
X
X	sprintf(source, "%s/.alias_hash", home);
X	sprintf(dest,   "%s/%s",          home, ALIAS_HASH);
X	link(source, dest);
X
X	sprintf(source, "%s/.alias_data", home);
X	sprintf(dest,   "%s/%s",          home, ALIAS_DATA);
X	link(source, dest);
X
X	printf("\n*** Moved all data files into %s/.elm directory ***\n\n",
X		home);
X}
SHAR_EOF
echo "File utils/newalias.c is complete"
chmod 0444 utils/newalias.c || echo "restore of utils/newalias.c fails"
echo "x - extracting utils/newmail.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > utils/newmail.c &&
X
Xstatic char rcsid[] = "@(#)$Id: newmail.c,v 2.16 89/03/25 21:47:55 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.16 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@dsinc.UUCP			dsinc!elm
X *
X *******************************************************************************
X * $Log:	newmail.c,v $
X * Revision 2.16  89/03/25  21:47:55  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X/** This is actually two programs folded into one - 'newmail()' and
X    'wnewmail()'.  They perform essentially the same function, to
X    monitor the mail arriving in a set of/a mailbox or folder, but
X    newmail is designed to run in background on a terminal, and
X    wnewmail is designed to have a window of its own to run in.
X
X    The main difference is that wnewmail checks for mail more often.
X
X    The usage parameters are:
X
X	-i <interval>  		how often to check for mail
X				(default: 60 secs if newmail,
X					  10 secs if wnewmail)
X
X	<filename>		name of a folder to monitor
X				(can prefix with '+'/'=', or can
X			 	default to the incoming mailbox)
X
X	<filename>=prefix	file to monitor, output with specified
X				prefix when mail arrives.
X
X    If we're monitoring more than one mailbox the program will prefix
X    each line output (if 'newmail') or each cluster of mail (if 'wnewmail')
X    with the basename of the folder the mail has arrived in.  In the 
X    interest of exhaustive functionality, you can also use the "=prefix"
X    suffix (eh?) to specify your own strings to prefix messages with.
X
X    The output format is either:
X
X	  newmail:
X	     >> New mail from <user> - <subject>
X	     >> Priority mail from <user> - <subject>
X
X	     >> <folder>: from <user> - <subject>
X	     >> <folder>: Priority from <user> - <subject>
X
X	  wnewmail:
X	     <user> - <subject>
X	     Priority: <user> - <subject>
X
X	     <folder>: <user> - <subject>
X	     <folder>: Priority: <user> - <subject>\fR
X
X**/
X
X#include <stdio.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#include "defs.h"
X
X#include <signal.h>	/* background jobs ignore some signals... */
X
Xstatic char ident[] = { WHAT_STRING };
X
X#define LINEFEED		(char) 10
X#define BEGINNING		0			/* seek fseek(3S) */
X#define DEFAULT_INTERVAL	60
X
X#define MAX_FOLDERS		25		/* max we can keep track of */
X
X#define NO_SUBJECT	"(No Subject Specified)"
X
X#define metachar(c)	(c == '+' || c == '=' || c == '%')
X
Xchar  *getusername();
Xlong  bytes();
X
Xstruct folder_struct {
X	  char		foldername[SLEN];
X	  char		prefix[NLEN];
X	  FILE 		*fd;
X	  long		filesize;
X       } folders[MAX_FOLDERS];
X
Xint  interval_time,		/* how long to sleep between checks */
X     debug = 0,			/* include verbose debug output?    */
X     in_window = 0,		/* are we running as 'wnewmail'?    */
X     total_folders = 0,		/* # of folders we're monitoring    */
X     current_folder = 0;	/* struct pointer for looping       */
X
Xchar hostname[SLEN],            /* name of machine we're on         */
X     hostdomain[SLEN];          /* name of domain we're in          */
X
X#ifdef BSD
Xextern int errno;
X#endif
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	extern char *optarg;
X	extern int   optind, opterr;
X	char *ptr;
X	int c, i, done;
X	long lastsize,
X	     newsize;			/* file size for comparison..      */
X
X#ifdef HOSTCOMPILED
X	strncpy(hostname, HOSTNAME, sizeof(hostname));
X#else
X	gethostname(hostname, sizeof(hostname));
X#endif
X	gethostdomain(hostdomain, sizeof(hostdomain));
X
X	interval_time = DEFAULT_INTERVAL;
X	opterr = 0;
X
X	/** let's see if the first character of the basename of the
X	    command invoked is a 'w' (e.g. have we been called as
X	    'wnewmail' rather than just 'newmail'?)
X	**/
X
X	for (i=0, ptr=(argv[0] + strlen(argv[0])-1); !i && ptr > argv[0]; ptr--)
X	  if (*ptr == '/') {
X	    in_window = (*(ptr+1) == 'w');
X	    i++;
X	  }
X
X	if (ptr == argv[0] && i == 0 && argv[0][0] == 'w')
X	  in_window = 1;
X
X	while ((c = getopt(argc, argv, "di:w")) != EOF) {
X	  switch (c) {
X	    case 'd' : debug++;					break;
X	    case 'i' : interval_time = atoi(optarg);		break;
X	    case 'w' : in_window = 1;				break;
X	    default  : usage();					exit(1);
X	 }
X	}
X
X	if (interval_time < 10)
X	  fprintf(stderr,
X"Warning: interval set to %d second%s.  I hope you know what you're doing!\n",
X	  interval_time, interval_time == 1 ? "" : "s");
X
X	/* now let's parse the foldernames, if any are given */
X
X	if (optind >= argc) /* get default */
X	  add_default_folder();
X	else {
X	  while (optind < argc)
X	    add_folder(argv[optind++]);
X	  pad_prefixes();			/* for nice output...*/
X	}
X
X#ifdef AUTO_BACKGROUND
X	if (! in_window) {
X	  if (fork())	    /* automatically puts this task in background! */
X	    exit(0);
X
X	  (void) signal(SIGINT, SIG_IGN);
X	  (void) signal(SIGQUIT, SIG_IGN);
X	}
X#endif
X	(void) signal(SIGHUP, SIG_DFL);
X
X	if (in_window && ! debug)
X	  printf("Incoming mail:\n");
X
X	while (1) {
X	
X#ifndef AUTO_BACKGROUND		/* won't work if we're nested this deep! */
X	  if (getppid() == 1) 	/* we've lost our shell! */
X	    exit();
X#endif
X	  if (! isatty(1))	/* we're not sending output to a tty any more */
X	     exit();
X
X	  if (debug) printf("\n----\n");
X
X	  for (i = 0; i < total_folders; i++) {
X
X	    if (debug)
X	      printf("[checking folder #%d: %s]\n", i, folders[i].foldername);
X
X	    if (folders[i].fd == (FILE *) NULL) {
X
X	      if ((folders[i].fd = fopen(folders[i].foldername,"r")) == NULL)
X	        if (errno == EACCES) {
X		   fprintf(stderr, "\nPermission to monitor %s denied!\n\n",
X			 folders[i].foldername);
X	           sleep(5);
X	           exit(1);
X	        }
X	    }
X
X	    if ((newsize = bytes(folders[i].foldername)) > 
X	        folders[i].filesize) {	/* new mail has arrived! */
X
X	      if (debug)
X	        printf(
X		   "\tnew mail has arrived!  old size = %ld, new size=%ld\n",
X		   folders[i].filesize, newsize);
X
X	      /* skip what we've read already... */
X
X	      if (fseek(folders[i].fd, folders[i].filesize, 
X			BEGINNING) != 0)
X	        perror("fseek()");
X
X	      folders[i].filesize = newsize;
X
X	      if (in_window)
X	        putchar((char) 007);		/* BEEP!*/
X	      else
X	        printf("\n\r");	/* blank lines surrounding message */
X
X	      read_headers(i);		/* read and display new mail! */
X
X	      if (! in_window)
X	        printf("\n\r");
X	    }
X	    else if (newsize != folders[i].filesize) {	/* file SHRUNK! */
X
X	      folders[i].filesize = bytes(folders[i].foldername);
X	      (void) fclose(folders[i].fd);	/* close it and ...         */
X	      folders[i].fd = (FILE *) NULL;	/* let's reopen the file    */
X
X	      lastsize = folders[i].filesize;
X	      done     = 0;
X
X	      while (! done) {
X	        sleep(0);	/* basically gives up our CPU slice */
X	        newsize = bytes(folders[i].foldername);
X	        if (newsize != lastsize)
X	          lastsize = newsize;
X		else
X	          done++;
X	      } 
X	        
X	      folders[i].filesize = newsize;
X	    }
X	  }
X
X	  sleep(interval_time);
X	}
X}
X
Xint
Xread_headers(current_folder)
Xint current_folder;
X{
X	/** read the headers, output as found given current_folder,
X	    the prefix of that folder, and whether we're in a window
X	    or not.
X	**/
X
X	char buffer[SLEN], from_whom[SLEN], subject[SLEN];
X	register int subj = 0, in_header = 1, count = 0, priority=0;
X
X	while (fgets(buffer, SLEN, folders[current_folder].fd) != NULL) {
X	  if (first_word(buffer,"From ")) {
X	    if (real_from(buffer, from_whom)) {
X	      subj = 0;
X	      priority = 0;
X	      in_header = 1;
X	      subject[0] ='\0';
X	      from_whom[0] ='\0';
X	    }
X	  }
X	  else if (in_header) {
X	    if (first_word(buffer,">From")) 
X	      forwarded(buffer, from_whom); /* return address */
X	    else if (first_word(buffer,"Subject:") ||
X		     first_word(buffer,"Re:")) {
X	      if (! subj++) {
X	        remove_first_word(buffer);
X		strcpy(subject, buffer);
X	      }
X	    }
X	    else if (first_word(buffer,"Priority:")) 
X	      priority++;
X	    else if (first_word(buffer,"From:")) 
X	      parse_arpa_from(buffer, from_whom);
X	    else if (buffer[0] == LINEFEED) {
X	      in_header = 0;	/* in body of message! */
X	      show_header(priority, from_whom, subject, current_folder);
X	      count++;
X	    }
X	  }
X	}
X	return(count);
X}
X
Xadd_folder(name)
Xchar *name;
X{
X	/* add the specified folder to the list of folders...ignore any
X	   problems we may having finding it (user could be monitoring
X	   a mailbox that doesn't currently exist, for example)
X	*/
X
X	char *cp, buf[SLEN];
X
X	if (current_folder > MAX_FOLDERS) {
X	  fprintf(stderr,
X              "Sorry, but I can only keep track of %d folders.\n", MAX_FOLDERS);
X	  exit(1);
X	}
X
X	/* now let's rip off the suffix "=<string>" if it's there... */
X
X	for (cp = name + strlen(name); cp > name+1 && *cp != '=' ; cp--)
X	  /* just keep stepping backwards */ ;
X
X	/* if *cp isn't pointing to the first character we'e got something! */
X
X	if (cp > name+1) {
X
X	  *cp++ = '\0';		/* null terminate the filename & get prefix */
X
X	  if (metachar(*cp)) cp++;
X
X	  strcpy(folders[current_folder].prefix, cp);
X	}
X	else {			/* nope, let's get the basename of the file */
X	  for (cp = name + strlen(name); cp > name && *cp != '/'; cp--)
X	    /* backing up a bit... */ ;
X
X	  if (metachar(*cp)) cp++;
X	  if (*cp == '/') cp++;
X
X	  strcpy(folders[current_folder].prefix, cp);
X	}
X
X	/* and next let's see what kind of weird prefix chars this user
X	   might be testing us with.  We can have '+'|'='|'%' to expand
X	   or a file located in the incoming mail dir...
X	*/
X
X	if (metachar(name[0]))
X	  expand_filename(name, folders[current_folder].foldername);
X	else if (access(name, 00) == -1) {
X	  /* let's try it in the mail home directory */
X	  sprintf(buf, "%s%s", mailhome, name);
X	  if (access(buf, 00) != -1) 		/* aha! */
X	    strcpy(folders[current_folder].foldername, buf);
X	  else
X	    strcpy(folders[current_folder].foldername, name);
X	}
X	else
X	  strcpy(folders[current_folder].foldername, name);
X
X	/* now let's try to actually open the file descriptor and grab
X	   a size... */
X
X	if ((folders[current_folder].fd = 
X	      fopen(folders[current_folder].foldername, "r")) == NULL)
X          if (errno == EACCES) {
X	    fprintf(stderr, "\nPermission to monitor \"%s\" denied!\n\n",
X			 folders[current_folder].foldername);
X	    exit(1);
X	  }
X
X	folders[current_folder].filesize = 
X	      bytes(folders[current_folder].foldername);
X
X	/* and finally let's output what we did */
X
X	if (debug)
X	  printf("folder %d: \"%s\" <%s> %s, size = %ld\n",
X	      current_folder,
X	      folders[current_folder].foldername,
X	      folders[current_folder].prefix,
X	      folders[current_folder].fd == NULL? "not found" : "opened",
X	      folders[current_folder].filesize);
X
X	/* and increment current-folder please! */
X
X	current_folder++;
X	total_folders++;
X}
X
Xadd_default_folder()
X{
X	char	*cp;
X
X	/* this routine will add the users home mailbox as the folder
X	 * to monitor.  Since there'll only be one folder we'll never
X	 * prefix it either...
X	 *	determine mail file from environment variable if found,
X	 *	else use password entry
X	 */
X	if ((cp = getenv("MAIL")) == NULL)
X	  sprintf(folders[0].foldername, "%s%s", mailhome, getusername());
X	else
X	  strcpy(folders[0].foldername, cp);
X	
X	folders[0].fd       = fopen(folders[0].foldername, "r");
X	folders[0].filesize = bytes(folders[0].foldername);
X
X	if (debug)
X	  printf("default folder: \"%s\" <%s> %s, size = %ld\n",
X	      folders[0].foldername,
X	      folders[0].prefix,
X	      folders[0].fd == NULL? "not found" : "opened",
X	      folders[0].filesize);
X
X	total_folders = 1;
X}
X
Xint
Xreal_from(buffer, who)
Xchar *buffer, *who;
X{
X	/***** returns true iff 's' has the seven 'from' fields,
X	       initializing the who to the sender *****/
X
X	char junk[SLEN];
X
X	junk[0] = '\0';
X	sscanf(buffer, "%*s %s %*s %*s %*s %*s %s",
X	            who, junk);
X	return(junk[0] != '\0');
X}
X
Xforwarded(buffer, who)
Xchar *buffer, *who;
X{
X	/** change 'from' and date fields to reflect the ORIGINATOR of 
X	    the message by iteratively parsing the >From fields... **/
X
X	char machine[SLEN], buff[SLEN];
X
X	machine[0] = '\0';
X	sscanf(buffer, "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %s",
X	            who, machine);
X
X	if(machine[0] == '\0')	/* try for address with timezone in date */
X	sscanf(buffer, "%*s %s %*s %*s %*s %*s %*s %*s %*s %s",
X	            who, machine);
X
X	if (machine[0] == '\0') /* try for srm address */
X	  sscanf(buffer, "%*s %s %*s %*s %*s %*s %*s %*s %s",
X	            who, machine);
X
X	if (machine[0] == '\0')
X	  sprintf(buff,"anonymous");
X	else
X	  sprintf(buff,"%s!%s", machine, who);
X
X	strncpy(who, buff, SLEN);
X}
X
X
Xremove_first_word(string)
Xchar *string;
X{	/** removes first word of string, ie up to first non-white space
X	    following a white space! **/
X
X	register int loc;
X
X	for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
X	    ;
X
X	while (string[loc] == ' ' || string[loc] == '\t')
X	  loc++;
X	
X	move_left(string, loc);
X}
X
Xmove_left(string, chars)
Xchar string[];
Xint  chars;
X{
X	/** moves string chars characters to the left DESTRUCTIVELY **/
X
X	register int i;
X
X	chars--; /* index starting at zero! */
X
X	for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
X	  string[i-chars] = string[i];
X
X	string[i-chars] = '\0';
X}
X
Xshow_header(priority, from, subject, current_folder)
Xint   priority;
Xchar *from, *subject;
Xint   current_folder;
X{
X	/** output header in clean format, including abbreviation
X	    of return address if more than one machine name is
X	    contained within it! **/
X	char buffer[SLEN];
X	int  loc, i=0, exc=0;
X
X#ifndef INTERNET
X	/* Remove bogus "@host.domain" string. */
X
X	sprintf(buffer, "@%s%s", hostname, hostdomain);
X
X	if (chloc(from, '!') != -1 && in_string(from, buffer))
X	  from[strlen(from) - strlen(buffer)] = '\0';
X#endif
X
X	loc = strlen(from);
X
X	while (exc < 2 && loc > 0)
X	  if (from[--loc] == '!')
X	    exc++;
X
X	if (exc == 2) { /* lots of machine names!  Get last one */
X	  loc++;
X	  while (loc < strlen(from) && loc < SLEN)
X	    buffer[i++] = from[loc++];
X	  buffer[i] = '\0';
X	  strcpy(from, buffer);
X	}
X
X	if (strlen(subject) < 2)
X	  strcpy(subject, NO_SUBJECT);
X	
X	if (in_window)
X	  if (total_folders > 1)
X	    printf("%s: %s%s -- %s\n",
X		   folders[current_folder].prefix,
X		   priority? "Priority " : "", from, subject);
X	  else
X	    printf("%s%s -- %s\n",
X		   priority? "Priority " : "", from, subject);
X	else
X	  if (total_folders > 1)
X	    printf(">> %s: %sail from %s - %s\n\r", 
X		  folders[current_folder].prefix,
X		  priority? "Priority m" : "M", from, subject);
X	  else
X	    printf(">> %sail from %s - %s\n\r", 
X		  priority? "Priority m" : "M", from, subject);
X}	
X
Xparse_arpa_from(buffer, newfrom)
Xchar *buffer, *newfrom;
X{
X	/** try to parse the 'From:' line given... It can be in one of
X	    two formats:
X		From: Dave Taylor <hpcnou!dat>
X	    or  From: hpcnou!dat (Dave Taylor)
X	    Change 'newfrom' ONLY if sucessfully parsed this entry and
X	    the resulting name is non-null! 
X	**/
X
X	char temp_buffer[SLEN], *temp;
X	register int i, j = 0, in_parens;
X
X	temp = (char *) temp_buffer;
X	temp[0] = '\0';
X
X	no_ret(buffer);		/* blow away '\n' char! */
X
X	if (lastch(buffer) == '>') {
X	  for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' &&
X	       buffer[i] != '('; i++)
X	    temp[j++] = buffer[i];
X	  temp[j] = '\0';
X	}
X	else if (lastch(buffer) == ')') {
X	  in_parens = 1;
X	  for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '<'; i--) {
X	    switch(buffer[i]) {
X	    case ')':	in_parens++;
X			break;
X	    case '(':	in_parens--;
X			break;
X	    }
X	    if(!in_parens) break;
X	    temp[j++] = buffer[i];
X	  }
X	  temp[j] = '\0';
X	  reverse(temp);
X	}
X
X	if (strlen(temp) > 0) {		/* mess with buffer... */
X
X	  /* remove leading spaces... */
X
X	  while (whitespace(temp[0]))
X	    temp = (char *) (temp + 1);		/* increment address! */
X
X	  /* remove trailing spaces... */
X
X	  i = strlen(temp) - 1;
X
X	  while (whitespace(temp[i]))
X	   temp[i--] = '\0';
X
X	  /* if anything is left, let's change 'from' value! */
X
X	  if (strlen(temp) > 0)
X	    strcpy(newfrom, temp);
X	}
X}
X
Xreverse(string)
Xchar *string;
X{
X	/** reverse string... pretty trivial routine, actually! **/
X
X	char buffer[SLEN];
X	register int i, j = 0;
X
X	for (i = strlen(string)-1; i >= 0; i--)
X	  buffer[j++] = string[i];
X
X	buffer[j] = '\0';
X
X	strcpy(string, buffer);
X}
X
Xlong
Xbytes(name)
Xchar *name;
X{
X	/** return the number of bytes in the specified file.  This
X	    is to check to see if new mail has arrived....  **/
X
X	int ok = 1;
X	extern int errno;	/* system error number! */
X	struct stat buffer;
X
X	if (stat(name, &buffer) != 0)
X	  if (errno != 2) {
X	    fprintf(stderr,"Error %d attempting fstat on %s", errno, name);
X	    exit(1);
X	  }
X	  else
X	    ok = 0;
X	
X	return(ok ? buffer.st_size : 0);
X}
X
Xchar  *getusername()
X{
X	/** Getting the username on some systems is a real pain, so...
X	   This routine is guaranteed to return a usable username **/
X
X	char *return_value, *getlogin(), *cuserid();
X
X	if ((return_value = getlogin()) == NULL)
X	  if ((return_value = cuserid(NULL)) == NULL) {
X	    printf("Newmail: I can't get username!\n");
X	    exit(1);
X	  }
X
X	return( (char *) return_value);
X}
X
Xusage()
X{
X	/* print a nice friendly usage message */
X
X	fprintf(stderr, 
X"\nUsage: %s [-d] [-i interval] [-w] {folders}\n");
X	fprintf(stderr, "\nWhere:\n");
X	fprintf(stderr, "  -d\tturns on debugging output\n");
X	fprintf(stderr,
X"  -i D\tsets the interval checking time to 'D' seconds\n");
X	fprintf(stderr,
X"  -w\tforces 'window'-style output, and bypasses auto-background\n\n");
X	fprintf(stderr,
X"folders can be specified by relative or absolute path names, can be the name\n");
X	fprintf(stderr,
X"of a mailbox in the incoming mail directory to check, or can have one of the\n");
X	fprintf(stderr,
X"standard Elm mail directory prefix chars (e.g. '+', '=' or '%').\n");
X	fprintf(stderr,
X"Furthermore, any folder can have '=string' as a suffix to indicate a folder\n");
X	fprintf(stderr,
X"identifier other than the basename of the file\n\n");
X}
X
X
Xexpand_filename(name, store_space)
Xchar *name, *store_space;
X{
X	strcpy(store_space, name);
X	if (expand(store_space) == 0) {
X	  fprintf(stderr,"Sorry, but I couldn't expand \"%s\"\n",name);
X	  exit(1);
X	}
X}
X
Xpad_prefixes()
X{
X	/** This simple routine is to ensure that we have a nice
X	    output format.  What it does is whip through the different
X	    prefix strings we've been given, figures out the maximum
X	    length, then space pads the other prefixes to match.
X	**/
X
X	register int i, j, len = 0;
X
X	for (i=0; i < total_folders; i++)
X	  if (len < (j=strlen(folders[i].prefix)))
X	    len = j;
X	
X	for (i=0; i < total_folders; i++)
X	  for (j = strlen(folders[i].prefix); j < len; j++)
X	    strcat(folders[i].prefix, " ");
X}
SHAR_EOF
chmod 0444 utils/newmail.c || echo "restore of utils/newmail.c fails"
echo "x - extracting utils/printmail (Text)"
sed 's/^X//' << 'SHAR_EOF' > utils/printmail &&
X: Use /bin/sh
X# printmail: part of the Elm mail system
X# @(#)$Id: printmail,v 2.1 89/03/03 17:46:47 syd Exp $
X 
Xif [ "$1" = "-p" ]; then
X  flags="-p";shift
Xfi
X 
Xif [ "$1" != "" ]; then
X  if [ ! -r $1 ]; then
X    echo printmail: cannot open folder $1 1>&2
X    exit 1
X  else
X    flags="$flags -f $1"
X  fi
Xfi
X 
X# and now invoke readmsg
Xexec readmsg $flags \*
SHAR_EOF
chmod 0444 utils/printmail || echo "restore of utils/printmail fails"
echo "x - extracting utils/readmsg.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > utils/readmsg.c &&
X
Xstatic char rcsid[] = "@(#)$Id: readmsg.c,v 2.7 89/03/25 21:47:57 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.7 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@dsinc.UUCP			dsinc!elm
X *
X *******************************************************************************
X * $Log:	readmsg.c,v $
X * Revision 2.7  89/03/25  21:47:57  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X/** This routine adds the functionality of the "~r" command to the Elm mail 
X    system while still allowing the user to use the editor of their choice.
X
X    The program, without any arguments, tries to read a file in the users home 
X    directory called ".readmsg" (actually defined in the sysdefs.h system 
X    defines file) and if it finds it reads the current message.  If it doesn't 
X    find it, it will return a usage error.
X
X    The program can also be called with an explicit message number, list of 
X    message numbers, or a string to match in the message (including the header).
X    NOTE that when you use the string matching option it will match the first 
X    message containing that EXACT (case sensitive) string and then exit.
X**/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "defs.h"
X
X/** three defines for what level of headers to display **/
X
X#define ALL		1
X#define WEED		2
X#define NONE		3
X
X#define metachar(c)	(c == '=' || c == '+' || c == '%')
X
Xstatic char ident[] = { WHAT_STRING };
X
X#define  MAX_LIST	25		/* largest single list of arguments */
X
X#define  LAST_MESSAGE	9999		/* last message in list ('$' char)  */
X#define  LAST_CHAR	'$'		/* char to delimit last message..   */
X#define  STAR		'*'		/* char to delimit all messages...  */
X
Xint read_message[MAX_LIST]; 		/* list of messages to read	    */
Xint messages = 0;			/* index into list of messages      */
X
Xint numcmp();				/* strcmp, but for numbers          */
Xchar *words(),				/* function defined below...        */
X     *expand_define();			/*     ditto...                     */
X
X
Xextern char *optarg;		/* for parsing the ... 		    */
Xextern int   optind;			/*  .. starting arguments           */
X
Xchar *getenv();				/* keep lint happy */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	FILE *file;			        /* generic file descriptor! */
X	char filename[SLEN], 			/* filename buffer          */
X	     infile[SLEN],			/* input filename	    */
X	     buffer[SLEN], 			/* file reading buffer      */
X	     string[SLEN],			/* string match buffer      */
X	     *cp;
X
X	int current_in_queue = 0, 		/* these are used for...     */
X	    current = 0,			/* ...going through msgs     */
X	    list_all_messages = 0,		/* just list 'em all??       */
X	    num, 				/* for argument parsing      */
X	    page_breaks = 0,			/* use "^L" breaks??         */
X            total,				/* number of msgs current    */
X	    include_headers = WEED, 		/* flag: include msg header? */
X	    last_message = 0, 			/* flag: read last message?  */
X	    not_in_header = 0,			/* flag: in msg header?      */
X	    string_match = 0;			/* flag: using string match? */
X
X	/**** start of the actual program ****/
X
X	while ((num = getopt(argc, argv, "nhf:p")) != EOF) {
X	  switch (num) {
X	    case 'n' : include_headers = NONE;		break;
X	    case 'h' : include_headers = ALL;		break;
X	    case 'f' : strcpy(infile, optarg);	
X		       if (metachar(infile[0]))
X	                 if (expand(infile) == 0)
X	                   printf("%s: couldn't expand filename %s!\n", 
X			          argv[0], infile);
X		       break;
X	    case 'p' : page_breaks++;			break;
X	    case '?' : printf(
X			"Usage: %s [-n|-h] [-f filename] [-p] <message list>\n",
X			 argv[0]);
X	  	       exit(1);
X	  }
X	}
X	
X	/** whip past the starting arguments so that we're pointing
X	    to the right stuff... **/
X
X	*argv++;	/* past the program name... */
X
X	while (optind-- > 1) {
X	  *argv++;
X	  argc--;
X	}
X
X	/** now let's figure out the parameters to the program... **/
X
X	if (argc == 1) {	/* no arguments... called from 'Elm'? */
X	  sprintf(filename, "%s/%s", getenv("HOME"), readmsg_file);
X	  if ((file = fopen(filename, "r")) != NULL) {
X	    fscanf(file, "%d", &(read_message[messages++]));
X	    fclose(file);
X	  }
X	  else {	/* no arguments AND no .readmsg file!! */
X	    fprintf(stderr,
X	        "Usage: readmsg [-n|-h] [-f filename] [-p] <message list>\n");
X	    exit(1);
X	  }
X	}
X	else if (! isdigit(*argv[0]) && *argv[0] != LAST_CHAR && 
X	         *argv[0] != STAR) {  
X	  string_match++;
X	 
X	  while (*argv)
X	    sprintf(string, "%s%s%s", string, string[0] == '\0'? "" : " ",
X		    *argv++);
X	}
X	else if (*argv[0] == STAR) 		/* all messages....   */
X	  list_all_messages++;
X	else { 					  /* list of nums   */
X
X	  while (--argc > 0) {
X	    num = -1;
X
X	    sscanf(*argv,"%d", &num);
X
X	    if (num < 0) {
X	      if (*argv[0] == LAST_CHAR) {
X	        last_message++;
X		num = LAST_MESSAGE;
X	      }
X	      else {
X	        fprintf(stderr,"I don't understand what '%s' means...\n",
X			*argv); 
X	       	exit(1); 
X	      }
X	    }
X	    else if (num == 0) {	/* another way to say "last" */
X	      last_message++;
X	      num = LAST_MESSAGE;
X	    }
X
X	    *argv++; 
X
X	    read_message[messages++] = num;
X	  }
X
X	  /** and now sort 'em to ensure they're in a reasonable order... **/
X
X	  qsort(read_message, messages, sizeof(int), numcmp);
X	}
X
X	/** Now let's get to the mail file... **/
X
X	if (strlen(infile) == 0) {
X	  if ((cp = getenv("MAIL")) == NULL)
X	    sprintf(infile, "%s/%s", mailhome, getenv("LOGNAME"));
X	  else
X	    strcpy(infile, cp);
X	}
X
X	if ((file = fopen(infile, "r")) == NULL) {
X	  printf("But you have no mail! [ file = %d ]\n", infile);
X	  exit(0);
X	}
X
X	/** Now it's open, let's display some 'ole messages!! **/
X
X	if (string_match || last_message) {   /* pass through it once */
X
X	  if (last_message) {
X	    total = count_messages(file);	/* instantiate count */
X	    for (num=0; num < messages; num++)
X	      if (read_message[num] == LAST_MESSAGE)
X		read_message[num] = total;
X	  }
X	  else if (string_match)
X	    match_string(file, string);		/* stick msg# in list */
X
X	  if (total == 0 && ! string_match) {
X	    printf("There aren't any messages to read!\n");
X	    exit(0);
X	  }
X	}
X
X 	/** now let's have some fun! **/
X	
X	while (fgets(buffer, SLEN, file) != NULL) {
X	  if (real_from(buffer)) {
X	    if (! list_all_messages) {
X	      if (current == read_message[current_in_queue])
X	        current_in_queue++;
X	      if (current_in_queue >= messages) 
X	        exit(0);
X	    }
X	    current++;
X	    not_in_header = 0;	/* we're in the header! */
X	  }
X	  if (current == read_message[current_in_queue] || list_all_messages)
X	    if (include_headers==ALL || not_in_header)
X	      printf("%s", buffer);
X	    else if (strlen(buffer) < 2) {
X	      not_in_header++;
X	      if (include_headers==WEED) 
X		list_saved_headers(page_breaks);
X	    }
X	    else if (include_headers==WEED)
X	      possibly_save(buffer); 	/* check to see if we want this */ 
X	}
X	
X	exit(0);
X}
X
Xint
Xcount_messages(file)
XFILE *file;
X{
X	/** Returns the number of messages in the file **/
X
X	char buffer[SLEN];
X	int  count = 0;
X
X	while (fgets(buffer, SLEN, file) != NULL)
X	  if (strncmp(buffer, "From ", 5) == 0)
X	    count++;
X
X	rewind( file );
X	return( count );
X}
X
Xmatch_string(mailfile, string)
XFILE *mailfile;
Xchar *string;
X{
X	/** Increment "messages" and put the number of the message
X	    in the message_count[] buffer until we match the specified 
X	    string... **/
X
X	char buffer[SLEN];
X	int  message_count = 0;
X
X	while (fgets(buffer, SLEN, mailfile) != NULL) {
X	  if (strncmp(buffer, "From ", 5) == 0)
X	    message_count++;
X
X	  if (in_string(buffer, string)) {
X	    read_message[messages++] = message_count;
X	    rewind(mailfile);	
X	    return;
X	  }
X	}
X
X	fprintf(stderr,"Couldn't find message containing '%s'\n", string);
X	exit(1);
X}
X
Xint 
Xnumcmp(a, b)
Xint *a, *b;
X{
X	/** compare 'a' to 'b' returning less than, equal, or greater
X	    than, accordingly.
X	 **/
X
X	return(*a - *b);
X}
X
Xstatic char from[SLEN], subject[SLEN], date[SLEN], to[SLEN];
X
Xpossibly_save(buffer)
Xchar *buffer;
X{
X	/** Check to see what "buffer" is...save it if it looks 
X	    interesting... We'll always try to get SOMETHING
X	    by tearing apart the "From " line...  **/
X
X	if (strncmp(buffer, "Date:", 5) == 0)
X	  strcpy(date, buffer);
X	else if (strncmp(buffer, "Subject:", 8) == 0)
X	  strcpy(subject,buffer);
X	else if (strncmp(buffer,"From:", 5) == 0)
X	  strcpy(from, buffer);
X	else if (strncmp(buffer,"To: ", 3) == 0)
X	  strncpy(to, buffer, SLEN);
X	else if (strncmp(buffer,"From ", 5) == 0) {
X	  sprintf(from, "From: %s\n", words(2,1, buffer));	
X	  sprintf(date,"Date: %s",    words(3,7, buffer));
X	  to[0] = '\0';
X	}
X}
X
Xlist_saved_headers(page_break)
Xint page_break;
X{
X	/** This routine will display the information saved from the
X	    message being listed...If it displays anything it'll end
X	    with a blank line... **/
X
X	register int displayed_line = FALSE;
X	static int messages_listed = 0;
X
X	if (messages_listed++) 
X	  if (page_break)
X	    putchar(FORMFEED);	
X	  else
X	    printf(
X"\n--------------------------------------------------------------------\n\n\n");
X
X	if (strlen(from)    > 0) { printf("%s", from);    displayed_line++;}
X	if (strlen(subject) > 0) { printf("%s", subject); displayed_line++;}
X	if (strlen(to)      > 0) { printf("%s", to);      displayed_line++;}
X	if (strlen(date)    > 0) { printf("%s", date);    displayed_line++;}
X	
X	if (displayed_line)
X	   putchar('\n');
X}
X
Xchar *words(word, num_words, buffer)
Xint word, num_words;
Xchar *buffer;
X{
X	/** Return a buffer starting at 'word' and containing 'num_words'
X	    words from buffer.  Assume white space will delimit each word.
X	**/
X
X	static char internal_buffer[SLEN];
X	char   *wordptr, *bufptr, mybuffer[SLEN], *strtok();
X	int    wordnumber = 0, copying_words = 0;
X
X	internal_buffer[0] = '\0';	/* initialize */
X
X	strcpy(mybuffer, buffer);
X	bufptr = (char *) mybuffer;	/* and setup */
X
X	while ((wordptr = strtok(bufptr, " \t")) != NULL) {
X	  if (++wordnumber == word) {
X	    strcpy(internal_buffer, wordptr);
X	    copying_words++;
X	    num_words--;
X	  }
X	  else if (copying_words) {
X	    strcat(internal_buffer, " ");
X	    strcat(internal_buffer, wordptr);
X	    num_words--;
X	  }
X
X	  if (num_words < 1) 
X	    return((char *) internal_buffer);
X
X	  bufptr = NULL;
X	}
X
X	return( (char *) internal_buffer);
X}
X
Xint
Xexpand(infile)
Xchar *infile;
X{
X	/** Expand the filename since the first character is a meta-
X	    character that should expand to the "maildir" variable
X	    in the users ".elmrc" file...
X
X	    Note: this is a brute force way of getting the entry out 
X	    of the .elmrc file, and isn't recommended for the faint 
X	    of heart!
X	**/
X
X	FILE *rcfile;
X	char  buffer[SLEN], *expanded_dir, *home, *getenv(), *bufptr;
X	int   foundit = 0;
X
X	bufptr = (char *) buffer;		/* same address */
X	
X	if ((home = getenv("HOME")) == NULL) {
X	  printf(
X	     "Can't expand environment variable $HOME to find .elmrc file!\n");
X	  exit(1);
X	}
X
X	sprintf(buffer, "%s/%s", home, elmrcfile);
X
X	if ((rcfile = fopen(buffer, "r")) == NULL) {
X	  printf("Can't open your \".elmrc\" file (%s) for reading!\n",
X		 buffer);
X	  exit(1);
X	}
X
X	while (fgets(buffer, SLEN, rcfile) != NULL && ! foundit) {
X	  if (strncmp(buffer, "maildir", 7) == 0 ||
X	      strncmp(buffer, "folders", 7) == 0) {
X	    while (*bufptr != '=' && *bufptr) 
X	      bufptr++;
X	    bufptr++;			/* skip the equals sign */
X	    while (whitespace(*bufptr) && *bufptr)
X	      bufptr++; 
X	    home = bufptr;		/* remember this address */
X
X	    while (! whitespace(*bufptr) && *bufptr != '\n')
X	      bufptr++;
X
X	    *bufptr = '\0';		/* remove trailing space */
X	     foundit++;
X	  }
X	}
X
X	fclose(rcfile);			/* be nice... */
X
X	if (! foundit) {
X	  printf("Couldn't find \"maildir\" in your .elmrc file!\n");
X	  exit(1);
X	}
X
X	/** Home now points to the string containing your maildir, with
X	    no leading or trailing white space...
X	**/
X
X	expanded_dir = expand_define(home);
X
X	sprintf(buffer, "%s%s%s", expanded_dir, 
X		(expanded_dir[strlen(expanded_dir)-1] == '/' ||
X		infile[0] == '/') ? "" : "/", (char *) infile+1);
X
X	strcpy(infile, buffer);
X}
X
Xchar *expand_define(maildir)
Xchar *maildir;
X{
X	/** This routine expands any occurances of "~" or "$var" in
X	    the users definition of their maildir directory out of
X	    their .elmrc file.
X
X	    Again, another routine not for the weak of heart or staunch
X	    of will!
X	**/
X
X	static char buffer[SLEN];	/* static buffer AIEE!! */
X	char   name[SLEN],		/* dynamic buffer!! (?) */
X	       *nameptr,	       /*  pointer to name??     */
X	       *value;		      /* char pointer for munging */
X
X	if (*maildir == '~') 
X	  sprintf(buffer, "%s%s", getenv("HOME"), ++maildir);
X	else if (*maildir == '$') { 	/* shell variable */
X
X	  /** break it into a single word - the variable name **/
X
X	  strcpy(name, (char *) maildir + 1);	/* hurl the '$' */
X	  nameptr = (char *) name;
X	  while (*nameptr != '/' && *nameptr) nameptr++;
X	  *nameptr = '\0';	/* null terminate */
X	  
X	  /** got word "name" for expansion **/
X
X	  if ((value = getenv(name)) == NULL) {
X	    printf("Couldn't expand shell variable $%s in .elmrc!\n", name);
X	    exit(1);
X	  }
X	  sprintf(buffer, "%s%s", value, maildir + strlen(name) + 1);
X	}
X	else
X	   strcpy(buffer, maildir);
X
X	return( ( char *) buffer);
X}
X
Xint
Xreal_from(buffer)
Xchar *buffer;
X{
X	/***** Returns true iff 's' has the seven 'from' fields, (or
X	       8 - some machines include the TIME ZONE!!!) *****/
X
X	char sixthword[STRING], seventhword[STRING],
X	     eighthword[STRING], ninthword[STRING];
X
X	/* From <user> <day> <month> <day> <hr:min:sec> <year> */
X
X	if(strncmp(buffer, "From ", 5) != 0)
X	  return(FALSE);
X
X	/* Extract 6th, 7th, 8th, and 9th words */
X	seventhword[0] = eighthword[0] = ninthword[0] = '\0';
X	sscanf(buffer, "%*s %*s %*s %*s %*s %s %s %s %s",
X	  sixthword, seventhword, eighthword, ninthword);
X
X	/* Not a from line if 6th word doesn't have colons for time field */
X	if(strlen(sixthword) < 3)
X	  return(FALSE);
X	if (sixthword[1] != ':' && sixthword[2] != ':')
X	  return(FALSE);		
X
X	/* Not a from line if there is no seventh word */
X	if(seventhword[0] == '\0')
X	  return(FALSE);
X
X	/* Not a from line if there is a ninthword */
X	if (eighthword[0] != '\0') {
X	  if(ninthword[0] != '\0')
X	    return(FALSE);	
X	}
X
X	return(TRUE);
X}
SHAR_EOF
chmod 0444 utils/readmsg.c || echo "restore of utils/readmsg.c fails"
rm -f s2_seq_.tmp
echo "You have unpacked the last part"
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.