[mod.sources] v09i010: ELM Mail System, Part10/19

sources-request@mirror.TMC.COM (03/10/87)

Submitted by: Dave Taylor <hplabs!taylor>
Mod.sources: Volume 9, Issue 10
Archive-name: elm2/Part10

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of archive 10 (of 19)."
# Contents:  doc/Form.guide src/alias.c src/date.c test/test.mail
#   utils/from.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"doc/Form.guide\" \(9794 characters\)
if test -f doc/Form.guide ; then 
  echo shar: Will not over-write existing file \"doc/Form.guide\"
else
sed "s/^X//" >doc/Form.guide <<'END_OF_doc/Form.guide'
X.PH ""
X\"
X\"  A guide to using the Elm Forms mode
X\"  format with 'tbl Forms.guide | troff -mm > Forms.format'
X\"  or something similar.
X\"  (C) Copyright 1986 Dave Taylor
X\"
X\"  Last modification: October 9th, 1986
X\"
X.SA 1
X.nr Hy 1
X.nr Pt 1
X.nr Pi 8
X.lg
X.HM 1 1
X.rs
X.ds HF 3  3  
X.ds HP 12 12 10 10 10
X.PF ""
X.de eb
X.sp -1	
X.nf
X\h'-.5n'\L'|\\nau-1'\l'\\n(.lu+1n\(ul'\L'-|\\nau+1'\l'|0u-.5n\(ul'
X.fi
X..
X.ce 99
X.sp 9
X.ps 20
X\fBElm Forms Mode Guide\fR
X.sp 7
X.ps 12
X\fIWhat Forms Mode is, how to use it to create
Xcustom forms, how to reply to forms, and how to
Xuse it for AT&T Mail messages\fR
X.sp 5
XDave Taylor
X.sp
XHewlett-Packard Laboratories
X1501 Page Mill Road
XPalo Alto CA
X94304
X.sp 
Xemail: taylor@hplabs.HP.COM or hplabs!taylor
X.sp 10
X.ps 18
X\fB\(co\fR\s12 Copyright 1986, 1987 by Dave Taylor
X.ps 10
X.SK
X.sp 5
X.ps 14
X\fBElm Forms Mode Guide\fR
X.PH "'Forms Mode Guide''version 1.5'
X.PF "''Page \\\\nP''"
X.nr P 1
X.sp
X.ps 10
X(version 1.5)
X.sp 2
XDave Taylor
X.sp
XHewlett-Packard Laboratories
X1501 Page Mill Road
XPalo Alto CA
X94304
X.sp 
Xemail: taylor@hplabs.HP.COM or hplabs!taylor
X.sp 2
X\*(DT
X.ce 0
X.sp 3
X.P
XWhile there are a lot of mail systems that allow the transmission 
Xof text and primitive pictures, to send and reply to more complex
Xforms is simply not possible.  \fBElm\fR, however, took
Xthe forms mode implemented as part of the AT&T Mail package and has
Xexpanded on it to be a smoothly fit part of the overall mail system.
X.sp
XForms mode gives you the ability to send 'template' files to people 
Xand receive the filled-in replies.  Let's look at an example right
Xoff.
X.sp 2
XSay we were going to use computer mail as a way to file defects with
Xsoftware.  There is a certain amount of information we want to be able
Xto collect when each report is made, and if it is in a specific format
Xwe can use programs to file the defects upon receipt.
X.sp
XThe form we'll try to emulate starts out looking like;
X.DS CB
X.mk a
X.mk a
X.sp
X.tl ''Defect Reporting Form''
X.sp
X.tl 'Program:_________________________'Version:___________________''
X.tl 'Operating System:________________'Version:___________________''
X.sp
X.tl 'Defect Type:_______________________''
X.sp
X.tl 'Date Found:_____________________''By Whom:_____________________'
X.tl 'Date Reported:__________________''Phone:_______________________'
X.sp
X.tl 'Description:______________________________________________________________'
X.tl '__________________________________________________________________________'
X.tl '__________________________________________________________________________'
X.tl '__________________________________________________________________________'
X.tl '__________________________________________________________________________'
X.sp
X.eb
X.DE
X.sp
XThis form can actually be created almost exactly as listed above in 
Xthe \fBElm\fR mail system by using your standard editor and can then
Xbe mailed about as needed.
X.sp
XLet's say that we want a bit more information, however, especially with 
Xfields like ``Defect Type'', we want to list all the recommended answers.
XTo create the actual form, we need merely to replace the underlines in
Xthe above form with spaces.  The multi-line comments can simply be 
Xindicated by a `:' by itself on a line;
X.DS CB
X.mk a
X.sp
X.tl ''Defect Reporting Form''
X.sp
X.tl 'Program:                         'Version:                   ''
X.tl 'Operating System:                'Version:                   ''
X.sp
X.tl '(Valid Defect Types are: user-error, doc-error, fatal, other)'
X.tl 'Defect Type:                       ''
X.sp
X.tl 'Date Found:                     ''By Whom:                     '
X.tl 'Date Reported:                  ''Phone:                       '
X.sp
X.tl 'Description'
X.tl ':'
X.sp
XThank you for filling in this form.
X.sp 4
X.eb
X.DE
X.sp
XAs we can see, it is quite simple to create forms!!  
X.sp 2
XNow that we have an idea what we're talking about, let's actually officially
Xdefine the system...
X.sp 2
X.ne 7
X.ps 12
X\fBForms Mode Specification\fR
X.ps 10
X.sp
X[Note that this is all taken from the document \fIStandard for Exchanging
XForms on AT&T Mail\fR, Version 1.9 of 6/7/86, from AT&T]
X.sp
XThe forms mode is really quite simple.  Simple enough that it is amazing
Xthat it hadn't been implemented before AT&T Mail came along!!
X.sp
XIn a nutshell, each field is delimited by a `:' followed by a number of
Xblank spaces or tabs that represent the valid size for that field.  That
Xis, if we have a line in the form like;
X.nf
X	``Phone (area-code):     Number:          '' 
X.fi
XThe area-code field will be limited to three characters and the number to nine.
X(this is kind of hard to see with the proportionally spaced formatted copy,
Xalas).
XThe only exception to the rule is that a `:' by itself on a line represents
Xa field that is as large as the user entering the data desires.
X.sp 2
XThe actual form that is transmitted, in AT&T Mail parlance, is a ``SIMPLE''
Xforms handler message (as opposed to the ``ADVANCED'' handler).  This means
Xthat it contains three sections;
X.nf
X
X	The Message Header
X
X	[OPTIONS-SECTION]
X	***
X	[FORMS-IMAGE]
X	***
X	[RULES-SECTION]
X
X.fi
X\fBElm\fR generates form messages with the ``options'' section filled out,
Xbut ignores it when receiving mail.  The filled out section is;
X.nf
X
X	WIDTH=80
X	TYPE=SIMPLE
X	OUTPUT=TEXT
X
X.fi
XThe FORMS-IMAGE section is that described above.  The RULES-SECTION can
Xcontain explicit rules about the possible values of each field, but 
Xthis is currently ignored by \fBElm\fR, being a ``SIMPLE'' forms mode
Xmail system.
X.sp
XForms also have the header ``Content-Type: mailform'' to indicate to the
Xmail system (either \fBElm\fR or AT&T Mail) that a form is being sent.
X.sp
X\fBElm\fR further indicates that a form has been received by having an
X``F'' as the status character in the header display section (instead of
X``N'' for new, etc).
X.sp 2
X.ne 5
X.ps 12
X\fBComposing and Sending a Form\fR
X.ps 10
X.sp
XThe first step to enable sending forms is to change the setting of
Xthe variable \fIforms\fR in your \fI.elmrc\fR file to ``ON''.  E.g.;
X.nf
X
X	forms = ON
X
X.fi
XThe next step is to send the message to someone using the `m' (\fImail\fR)
Xcommand.  This then will drop you into an editor.  Type in the form as
Xindicated above, with appropriate colons and comments, and end the entry
Xby leaving the editor.
X.sp
XThe prompt is now;
X.nf
X
X.tl ''Choose: E)dit msg, edit H)eaders, M)ake form, S)end or F)orget : @''
X
X.fi
Xso we choose `m' - \fIMake form\fR.  The program then will either
Xrewrite the prompt without the M)ake form option, indicating that
Xthe form has been accepted, or will indicate the problem and give you
Xa chance to correct it.
X.sp
XOnce it has been accepted, simple use the `s' - \fIsend message\fR -
Xcommand and it's off!
X.sp 
XNote that you cannot reply to a message with a Form.
X.sp 2
X.ne 6
X.ps 12
X\fBReplying to a Form\fR
X.ps 10
X.sp
XLet's reply to the form message we generated now.  The header page of the
X\fBElm\fR mail system will indicate that the message is a form by having
Xan `F' next to it.  So we use `r' to reply and the screen is immediately
Xcleared and we're prompted, field by field, for the data requested.
XEach field has underscores in the input area to indicate the size field that
Xis expected.
X.sp
XAfter answering all the questions we'll have a screen that looks like;
X.DS CB
X.mk a
X.sp
X.tl ''Defect Reporting Form''
X.sp
XProgram: \fBThe Elm Mail System\fR____________
XVersion: \fB1.5\fR_____________________
XOperating System: \fBHP-UX\fR_________________
XVersion: \fB5.141 C\fR__________________
X.sp
X(Valid Defect Types are: user-error, doc-error, fatal, other)
XDefect Type: \fBfatal\fR____________________________________________________
X.sp
XDate Found: \fB10/9/86\fR_____________________
XBy Whom: \fBDave Taylor\fR_______________
XDate Reported: \fB10/9/86\fR__________________
XPhone: \fB(415) 857-6887\fR______________
X.sp
XDescription
X(Enter as many lines as needed, ending with a `.' by itself on a line)
X
X  \fBWhen running it on a CPM system I cannot compile successfully.\fR
X  \fB.\fR
X
XThank you for filling in this form.
X
X.tl ''Choose: E)dit form, edit H)eaders, S)end or F)orget : @''
X.sp 
X.eb
X.DE
X.sp
XQuite simple.  Notice, however, that the order of prompting is left to
Xright on each line, so the fields that on the form are placed in what
Xseems like a logical place, ``By Whom:'' and ``Phone:'' turn out to be
Xconfusing when filling in the actual form since it isn't clear what
X``Phone:'' is being asked for because of the intervention of the
X``Date Reported:'' field.
X.sp 
XThe message that will actually be sent out from this will have the
Xfields in a more acceptable format;
X.DS
X.mk a
X.sp
XWIDTH=80
XTYPE=SIMPLE
XOUTPUT=TEXT
X***
X.tl ''Defect Reporting Form''
X.sp
X.tl 'Program: The Elm Mail System     'Version: 1.5               ''
X.tl 'Operating System: HP-UX          'Version: 5.141 C           ''
X.sp
X.tl '(Valid Defect Types are: user-error, doc-error, fatal, other)'
X.tl 'Defect Type: fatal''
X.sp
X.tl 'Date Found: 10/9/86             ''By Whom: Dave Taylor         '
X.tl 'Date Reported: 10/9/86          ''Phone: (415) 857-6887        '
X.sp
X.tl 'Description'
X.sp
X    When running it on a CPM system I cannot compile successfully.
X.sp 2
XThank you for filling in this form.
X.sp
X***
X.sp
X.eb
X.DE
X.ne 5
X.sp 2
X.ps 12
X\fBComments on Forms Mode\fR
X.ps 10
X.sp
XAs was said at the beginning, this way of sending about forms could
Xprove to be very helpful and useful in a variety of contexts.  On the
Xother hand, until a more sophisticated forms language is used for the
Xforms, this should be sufficient to embody the power of the idea.
X.sp
XI welcome any comments and thoughts on this system and also welcome
Xpossible enhancements.
X.sp
XI also gratefully thank Dale DeJager of AT&T Information Systems 
Xfor sending me more
Xinformation on AT&T Mail than I could possibly digest in any finite
Xamount of time.
END_OF_doc/Form.guide
if test 9794 -ne `wc -c <doc/Form.guide`; then
    echo shar: \"doc/Form.guide\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/alias.c\" \(9636 characters\)
if test -f src/alias.c ; then 
  echo shar: Will not over-write existing file \"src/alias.c\"
else
sed "s/^X//" >src/alias.c <<'END_OF_src/alias.c'
X/**			alias.c			**/
X
X/** This file contains alias stuff
X
X    (C) Copyright 1986 Dave Taylor
X**/
X
X#include "headers.h"
X#include <errno.h>
X
Xchar *expand_group(), *get_alias_address(), *expand_system(), *get_token();
Xchar *error_name(), *error_description();
X
Xunsigned long sleep();
X
Xextern int errno;
X
Xextern int  findnode_has_been_initialized;
X
Xread_alias_files()
X{
X	/** read the system and user alias files, if present.
X	    Set the flags 'systemfiles' and 'userfiles' accordingly.
X	**/
X
X	char fname[SLEN];
X	int  hash;
X
X	if ((hash = open(system_hash_file, O_RDONLY)) == -1) {
X	 dprint1(1,
X		"Warning: Can't read system hash file %s (read_alias_files)\n",
X		system_hash_file);
X	  goto user;
X	}
X
X	read(hash, system_hash_table, sizeof system_hash_table);
X	close(hash);
X
X	/* and data file opened.. */
X
X	if ((system_data = open(system_data_file, O_RDONLY)) == -1) {
X	 dprint1(1, 
X	         "Warning: Can't read system data file %s (read_alias_files)\n",
X		 system_data_file);
X	  goto user;
X	}
X
X	system_files++;	/* got the system files! */
X
Xuser:   sprintf(fname,  "%s/.alias_hash", home); 
X
X	if ((hash = open(fname, O_RDONLY)) == -1) {
X	 dprint1(1,"Warning: Can't read user hash file %s (read_alias_files)\n",
X		  fname);
X	  return;
X	}
X
X	read(hash, user_hash_table, sizeof user_hash_table);
X	close(hash);
X
X	sprintf(fname,  "%s/.alias_data", home); 
X
X	if ((user_data = open(fname, O_RDONLY)) == -1) {
X	 dprint1(1,
X	         "Warning: can't read user data file %s  (read_alias_files)\n",
X		 fname);
X	  return;
X	}
X
X	user_files++;	/* got user files too! */
X}
X
Xint
Xadd_alias()
X{
X	/** add an alias to the user alias text file.  Return zero 
X	    if alias not added in actuality **/
X
X	char name[SLEN], *address, address1[LONG_STRING];
X	char comment[SLEN];
X	char *strcpy();
X
X	PutLine0(LINES-2,0,"Enter alias name: ");
X	CleartoEOLN();
X	Raw(OFF);
X	gets(name);
X	Raw(ON);
X	if (strlen(name) == 0) 
X	  return(0);
X	if ((address = get_alias_address(name, 0, 0)) != NULL) {
X	  dprint1(2, "Attempt to add a duplicate alias [%s] (add_alias)\n",
X		   address); 
X	  if (address[0] == '!') {
X	    address[0] = ' ';
X	    error1("already a group with that name:%s", address);
X	  }
X	  else
X	    error1("already an alias for that: %s", address);
X	  return(0);
X	}
X	PutLine1(LINES-2,0,"Full name for %s: ", name);
X	CleartoEOLN();
X	Raw(OFF);
X	gets(comment);
X	Raw(ON);
X	if (strlen(comment) == 0) strcpy(comment, name);  
X	PutLine1(LINES-2,0,"Enter address for %s: ",name);
X	CleartoEOLN();
X	Raw(OFF);
X	gets(address1);
X	Raw(ON);
X	if (strlen(address1) == 0) {
X	  error("No address specified!");
X	  return(0);
X	}
X	add_to_alias_text(name, comment, address1);
X	return(1);
X}
X
Xint
Xadd_current_alias()
X{
X	/** alias the current message to the specified name and
X	    add it to the alias text file, for processing as
X	    the user leaves the program.  Returns non-zero iff
X	    alias actually added to file **/
X
X	char name[SLEN], address1[LONG_STRING], buffer[LONG_STRING], *address;
X	char comment[SLEN];
X
X	if (current == 0) {
X	 dprint0(3,"Add current alias called without any current message!\n");
X	 error("No message to alias to!");
X	 return(0);
X	}
X	
X	PutLine0(LINES-2,0,"Current message address aliased to: ");
X	CleartoEOLN();
X	Raw(OFF);
X	gets(name);
X	Raw(ON);
X	if (strlen(name) == 0)	/* cancelled... */
X	  return(0);
X	if ((address = get_alias_address(name, 0, 0)) != NULL) {
X	 dprint1(3,
X	         "Attempt to add a duplicate alias [%s] (add_current_alias)\n",
X		 address); 
X	  if (address[1] == '!') {
X	    address[0] = ' ';
X	    error1("already a group with that name:%s", address);
X	  }
X	  else 
X	    error1("already an alias for that: %s", address); 
X          return(0);
X	}
X	PutLine1(LINES-2,0,"Full name of %s: ", name);
X	CleartoEOLN();
X	Raw(OFF);
X	gets(comment);
X	Raw(ON);
X	get_return(buffer);	/* grab the return address of this message */
X	strcpy(address1, strip_parens(buffer));	/* remove parens! */
X#ifndef DONT_OPTIMIZE_RETURN
X	optimize_return(address1);
X#endif
X	PutLine3(LINES-2,0,"%s (%s) = %s", comment, name, address1);
X	CleartoEOLN();
X	add_to_alias_text(name, comment, address1);
X	return(1);
X}
X
Xadd_to_alias_text(name, comment, address)
Xchar *name, *comment, *address;
X{
X	/** Add the data to the user alias text file.  Return zero if we
X	    succeeded, 1 if not **/
X	
X	FILE *file;
X	char fname[SLEN];
X	
X	sprintf(fname,"%s/.alias_text", home);
X	
X	if ((file = fopen(fname, "a")) == NULL) {
X	  dprint2(2, "FILE Failure attempting to add alias to file %s (%s)",
X		   fname, "add_to_alias_text");
X	  dprint2(2, "** %s - %s **\n", error_name(errno), 
X		   error_description(errno));
X	  error1("couldn't open %s to add new alias!", fname);
X	  return(1);
X	}
X
X	fprintf(file,"%s : %s : %s\n", name, comment, address);
X	fclose(file);
X
X	chown(fname, userid, groupid);
X
X	return(0);
X}
X
Xshow_alias_menu()
X{
X	MoveCursor(LINES-7,0); CleartoEOLN();	
X	MoveCursor(LINES-6,0); CleartoEOLN();	
X	MoveCursor(LINES-5,0); CleartoEOLN();
X	
X	PutLine0(LINES-7,COLUMNS-45, "Alias commands");
X	Centerline(LINES-5,
X"A)lias current msg, Check a P)erson or S)ystem, M)ake new alias, or R)eturn"
X	);
X}
X
Xalias()
X{
X	/** work with alias commands... **/
X	char name[NLEN], *address, ch, buffer[SLEN];
X	int  newaliases = 0;
X
X	if (mini_menu)
X	  show_alias_menu();
X
X	/** now let's ensure that we've initialized everything! **/
X
X#ifndef DONT_TOUCH_ADDRESSES
X	
X	if (! findnode_has_been_initialized) {
X	  if (! mail_only)
X	    error("initializing internal tables...");
X#ifndef USE_DBM
X	  get_connections();
X	  open_domain_file();
X#endif
X	  init_findnode();
X	  clear_error();
X          findnode_has_been_initialized = TRUE;
X	}
X
X#endif
X
X	define_softkeys(ALIAS);
X
X	while (1) {
X	  prompt("Alias: ");
X	  CleartoEOLN();
X	  ch = ReadCh();
X	  MoveCursor(LINES-1,0); CleartoEOS();
X	  
X	  dprint1(2,"\n-- Alias command: %c\n\n", ch);
X
X	  switch (tolower(ch)) {
X	    case '?': alias_help();				break;
X
X	    case 'a': newaliases += add_current_alias();	break;
X	    case 'm': newaliases += add_alias(); 		break;
X
X	    case RETURN:
X	    case LINE_FEED:
X	    case 'q':
X	    case 'x':
X	    case 'r': if (newaliases) install_aliases();
X		      return;
X	    case 'p': if (newaliases) 
X			error("Warning: new aliases not installed yet!");
X		      PutLine0(LINES-2,0,"Check for person: ");
X		      CleartoEOLN();
X		      Raw(OFF);
X	              gets(name);
X		      Raw(ON);
X		      if ((address = get_alias_address(name, 0, 0))!=NULL) {
X	                if (address[0] == '!') {
X	                  address[0] = ' ';
X	                  PutLine1(LINES-1,0,"Group alias:%-60.60s", address);
X	                  CleartoEOLN();
X		        }
X			else
X			  PutLine1(LINES-1,0,"Aliased address: %-60.60s", 
X			  address);
X		      }
X	              else 
X			error("not found");
X		      break;
X
X	    case 's': PutLine0(LINES-2,0,"Check for system: ");
X		      CleartoEOS();
X		      Raw(OFF);
X	              gets(name);
X		      Raw(ON);
X		      if (talk_to(name)) 
X#ifdef INTERNET_ADDRESS_FORMAT
X			PutLine1(LINES-1,0,
X		"You have a direct connection - the address is (user)@%s", 
X			name);
X#else
X			PutLine1(LINES-1,0,
X		"You have a direct connection - the address is %s!(user)", 
X			name);
X#endif
X		      else {
X		        sprintf(buffer, "(user)@%s", name);
X#ifdef DONT_TOUCH_ADDRESS
X	 	        strcpy(address, buffer);
X#else
X	 	        address = expand_system(buffer, FALSE);
X#endif
X		        if (strlen(address) > strlen(name) + 7)
X		          PutLine1(LINES-1,0,"Address is: %.65s", address);
X		        else
X		          error1("couldn't expand system '%s'", name);
X		      }
X		      break;
X
X	    case '@': PutLine0(LINES-2,0,"Fully expand alias: ");
X		      CleartoEOS();
X		      Raw(OFF);
X	              gets(name);
X		      Raw(ON);
X		      if ((address = get_alias_address(name, 1, 0)) != NULL) {
X	                ClearScreen();
X			PutLine1(3,0,"Aliased address:\n\r%s", address);
X	                PutLine0(LINES-1,0,"Press <return> to continue: ");
X			(void) getchar();
X		      }
X	              else 
X			error("not found");
X		      if (mini_menu) show_alias_menu();
X		      break;
X	    default : error("Invalid input!");
X	  }
X	}
X}
X
Xinstall_aliases()
X{
X	/** run the 'newalias' program and install the newly
X	    added aliases before going back to the main
X	    program! 
X	**/
X
X
X	error("Adding new aliases...");
X	sleep(2);
X
X	if (system_call(newalias, SH) == 0) {
X	  error("Re-reading the database in...");
X	  sleep(2);
X	  read_alias_files();
X	  set_error("New aliases installed successfully");
X	}
X	else
X	  set_error("'newalias' failed.  Please check alias_text");
X}
X
Xalias_help()
X{
X	/** help section for the alias menu... **/
X
X	char ch;
X
X	MoveCursor(LINES-3, 0);	CleartoEOS();
X
X	if (! mini_menu)
X	  lower_prompt("Key you want help for : ");
X	else {
X	  Centerline(LINES-3, 
X"Enter key you want help for, '?' for list or '.' to leave help");
X	  lower_prompt("Key : ");
X	}
X
X	while ((ch = tolower(ReadCh())) != '.') {
X	  switch(ch) {
X	    case '?' : display_helpfile(ALIAS_HELP);	
X	               if (mini_menu) show_alias_menu();	return;
X	    case 'a': error(
X"a = Add return address of current message to alias database");	break;
X	    case 'm': error(
X"m = Make new user alias, adding to alias database when done");	break;
X
X	    case RETURN:
X	    case LINE_FEED:
X	    case 'q':
X	    case 'x':
X	    case 'r': error("return from alias menu");		break;
X		      
X	    case 'p': error(
X"p = check for a person in the alias database");		break;
X	
X	    case 's': error(
X"s = check for a system in the host routing/domain database");	break;
X	
X	    default : error("That key isn't used in this section");	break;
X	    case '.': return;
X	  }
X	  if (! mini_menu)
X	    lower_prompt("Key you want help for : ");
X	  else 
X	    lower_prompt("Key : ");
X	}
X}
END_OF_src/alias.c
if test 9636 -ne `wc -c <src/alias.c`; then
    echo shar: \"src/alias.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/date.c\" \(10281 characters\)
if test -f src/date.c ; then 
  echo shar: Will not over-write existing file \"src/date.c\"
else
sed "s/^X//" >src/date.c <<'END_OF_src/date.c'
X/**		date.c		**/
X
X/** return the current date and time in a readable format! **/
X/** also returns an ARPA RFC-822 format date...            **/
X
X/** (C) Copyright 1985, Dave Taylor **/
X
X#include "headers.h"
X
X#ifdef BSD
X#  ifndef BSD4.1
X#    include <sys/time.h>
X#  else
X#    include <time.h>
X#    include <sys/types.h>
X#    include <sys/timeb.h>
X#  endif
X#else
X#  include <time.h>
X#endif
X
X#include <ctype.h>
X
X#ifdef BSD
X#undef toupper
X#undef tolower
X#endif
X
X#define MONTHS_IN_YEAR	11	/* 0-11 equals 12 months! */
X#define FEB		 1	/* 0 = January 		  */
X#define DAYS_IN_LEAP_FEB 29	/* leap year only 	  */
X
X#define ampm(n)		(n > 12? n - 12 : n)
X#define am_or_pm(n)	(n > 11? (n > 23? "am" : "pm") : "am")
X#define leapyear(year)	((year % 4 == 0) && (year % 100 != 0))
X
Xchar *monname[] = { "January", "February", "March", "April", "May", "June",
X		  "July", "August", "September", "October", "November",
X		  "December", ""};
X
Xchar *arpa_dayname[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
X		  "Fri", "Sat", "" };
X
Xchar *arpa_monname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X		  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""};
X
Xint  days_in_month[] = { 31,    28,    31,    30,    31,     30, 
X		  31,     31,    30,   31,    30,     31,  -1};
X
X#ifdef BSD
X  char *timezone();
X#else
X  extern char *tzname[];
X#endif
X
Xchar *shift_lower(), *strcpy(), *strncpy();
X
Xchar *get_arpa_date()
X{
X	/** returns an ARPA standard date.  The format for the date
X	    according to DARPA document RFC-822 is exemplified by;
X
X	       	      Mon, 12 Aug 85 6:29:08 MST
X
X	**/
X
X	static char buffer[SLEN];	/* static character buffer       */
X	struct tm *the_time,		/* Time structure, see CTIME(3C) */
X		  *localtime();
X	long	   junk,		/* time in seconds....		 */
X	    	   time();
X
X#ifdef BSD
X# ifdef BSD4.1
X	struct timeb	loc_time;
X
X	junk = time(long *) 0);
X	ftime(&loc_time);
X# else
X	struct  timeval  time_val;		
X	struct  timezone time_zone;
X
X	gettimeofday(&time_val, &time_zone);
X	junk = time_val.tv_sec;
X# endif
X
X#else
X	junk = time((long *) 0);	/* this must be here for it to work! */
X#endif
X
X	the_time = localtime(&junk);
X
X	sprintf(buffer, "%s, %d %s %d %d:%02d:%02d %s",
X	  arpa_dayname[the_time->tm_wday],
X	  the_time->tm_mday % 32,
X	  arpa_monname[the_time->tm_mon],
X	  the_time->tm_year % 100,
X	  the_time->tm_hour % 24,
X	  the_time->tm_min  % 61,
X	  the_time->tm_sec  % 61,
X#ifdef BSD
X# ifdef BSD4.1
X	  timezone(time_zone.timezone, the_time->tm_isdst));
X# else
X	  timezone(time_zone.tz_minuteswest, the_time->tm_isdst));
X# endif
X#else
X	  tzname[the_time->tm_isdst]);
X#endif
X	
X	return( (char *) buffer);
X}
X
Xchar *full_month(month)
Xchar *month;
X{
X	/** Given a three letter month abbreviation, return the 
X	    full name of the month.   If can't figure it out, just
X	    return the given argument. **/
X
X	char   name[4];
X	char   *shift_lower();
X	register int i;
X
X	/** ensure name in correct case... **/
X
X	strncpy(name, shift_lower(month), 3);
X	name[0] = toupper(name[0]);
X
X	/** now simply step through arpa_monname table to find a match **/
X
X	for (i=0; i < 12; i++)
X	  if (strncmp(name, arpa_monname[i], 3) == 0)
X	    return((char *) monname[i]);
X	
X	dprint1(2, "Warning: Couldn't expand monthname %s (full_month)\n", 
X	        month);
X
X	return( (char *) month);
X}
X
Xdays_ahead(days, buffer)
Xint days;
Xchar *buffer;
X{
X	/** return in buffer the date (Day, Mon Day, Year) of the date
X	    'days' days after today. **/
X
X	struct tm *the_time,		/* Time structure, see CTIME(3C) */
X		  *localtime();
X	long	   junk,		/* time in seconds....		 */
X		   time();
X
X	junk = time((long *) 0);	/* this must be here for it to work! */
X	the_time = localtime(&junk);
X
X	/* increment the day of the week */
X
X	the_time->tm_wday = (the_time->tm_wday + days) % 7;
X
X	/* the day of the month... */
X	the_time->tm_mday += days;
X	
X	if (the_time->tm_mday > days_in_month[the_time->tm_mon]) {
X	  if (the_time->tm_mon == FEB && leapyear(the_time->tm_year)) {
X	    if (the_time->tm_mday > DAYS_IN_LEAP_FEB) {
X	      the_time->tm_mday -= days_in_month[the_time->tm_mon];
X	      the_time->tm_mon += 1;
X	    }
X	  }
X	  else {
X	    the_time->tm_mday -= days_in_month[the_time->tm_mon];
X	    the_time->tm_mon += 1;
X	  }
X	}
X
X	/* check the month of the year */
X	if (the_time->tm_mon > MONTHS_IN_YEAR) {
X	  the_time->tm_mon -= MONTHS_IN_YEAR;
X	  the_time->tm_year += 1;
X	}
X
X	/* now, finally, build the actual date string */
X
X	sprintf(buffer, "%s, %d %s %d",
X	  arpa_dayname[the_time->tm_wday],
X	  the_time->tm_mday % 32,
X	  arpa_monname[the_time->tm_mon],
X	  the_time->tm_year % 100);
X}
X
Xint
Xvalid_date(day, mon, year)
Xchar *day, *mon, *year;
X{
X	/** Validate the given date - returns TRUE iff the date
X	    handed is reasonable and valid.  
X	    Ignore month param, okay? 
X	**/
X
X	register int daynum, yearnum;
X
X	daynum = atoi(day);
X	yearnum = atoi(year);
X	
X	if (daynum < 1 || daynum > 31) {
X	  dprint1(3, "Error: day %d is obviously wrong! (valid_date)\n", 
X	          daynum);
X	  return(0);
X	}
X	
X	if (yearnum < 1 || (yearnum > 100 && yearnum < 1900) ||
X	    yearnum > 2000) {
X	  dprint1(3, "Error: year %d is obviously wrong! (valid_date)\n", 
X		yearnum);
X	  return(0);
X	}
X	
X	return(1);
X}
X
Xfix_date(entry)
Xstruct header_rec *entry;
X{
X	/** This routine will 'fix' the date entry for the specified
X	    message.  This consists of 1) adjusting the year to 0-99
X	    and 2) altering time from HH:MM:SS to HH:MM am|pm **/ 
X
X	if (atoi(entry->year) > 99) 	
X	  sprintf(entry->year,"%d", atoi(entry->year) - 1900);
X
X	fix_time(entry->time);
X}
X
Xfix_time(timestring)
Xchar *timestring;
X{
X	/** Timestring in format HH:MM:SS (24 hour time).  This routine
X	    will fix it to display as: HH:MM [am|pm] **/
X
X	int hour, minute;
X
X	sscanf(timestring, "%d:%d", &hour, &minute);
X
X	if (hour < 1 || hour == 24) 
X	  sprintf(timestring, "12:%2.2d (midnight)", minute);
X	else if (hour < 12)
X	  sprintf(timestring, "%d:%2.2d am", hour, minute);
X	else if (hour == 12)
X	  sprintf(timestring, "%d:%2.2d (noon)", hour, minute);
X	else if (hour < 24)
X	  sprintf(timestring, "%d:%2.2d pm", hour-12, minute);
X}
X
Xint
Xcompare_dates(rec1, rec2)
Xstruct header_rec *rec1, *rec2;
X{
X	/** This function works similarly to the "strcmp" function, but
X	    has lots of knowledge about the internal date format...
X	    Apologies to those who "know a better way"...
X	**/
X
X	int month1, day1, year1, hour1, minute1,
X	    month2, day2, year2, hour2, minute2;
X
X	year1 = atoi(rec1->year);
X	year2 = atoi(rec2->year);
X
X	if (year1 != year2)
X	  return( year1 - year2 );
X
X	/* And HERE's where the performance of this sort dies... */
X
X	month1 = month_number(rec1->month);	/* retch...  gag....  */
X	month2 = month_number(rec2->month);	/*    puke...         */
X
X	if (month1 == -1) 
X	  dprint1(2,"month_number failed on month '%s'\n", rec1->month);
X
X	if (month2 == -1) 
X	  dprint1(2,"month_number failed on month '%s'\n", rec2->month);
X
X	if (month1 != month2)
X	  return( month1 - month2 );
X
X	/* back and cruisin' now, though... */
X
X	day1 = atoi(rec1->day);	 /* unfortunately, 2 is greater than 19  */
X	day2 = atoi(rec2->day);  /* on a dump string-only compare...     */
X
X	if (day1 != day2)
X	  return( day1 - day2 );
X
X	/* we're really slowing down now... */
X
X	minute1 = minute2 = -1;
X
X	sscanf(rec1->time, "%d:%d", &hour1, &minute1);
X	sscanf(rec2->time, "%d:%d", &hour2, &minute2);
X
X	/* did we get the time?  If not, try again */
X
X	if (minute1 < 0)
X	  sscanf(rec1->time, "%2d%2d", &hour1, &minute1);
X
X	if (minute2 < 0)
X	  sscanf(rec2->time, "%2d%2d", &hour2, &minute2);
X
X	/** deal with am/pm, if present... **/
X
X	if (strlen(rec1->time) > 3)
X	  if (rec1->time[strlen(rec1->time)-2] == 'p')
X	    hour1 += 12;
X
X	if (strlen(rec2->time) > 3)
X	  if (rec2->time[strlen(rec2->time)-2] == 'p')
X	    hour2 += 12;
X
X	if (hour1 != hour2)
X	  return( hour1 - hour2 );
X
X	return( minute1 - minute2 );		/* ignore seconds... */
X}
X
Xint
Xcompare_parsed_dates(rec1, rec2)
Xstruct date_rec rec1, rec2;
X{
X	/** This function is very similar to the compare_dates
X	    function but assumes that the two record structures
X	    are already parsed and stored in "date_rec" format.
X	**/
X
X	if (rec1.year != rec2.year)
X	  return( rec1.year - rec2.year );
X	
X	if (rec1.month != rec2.month)
X	  return( rec1.month - rec2.month );
X
X	if (rec1.day != rec2.day)
X	  return( rec1.day - rec2.day );
X
X	if (rec1.hour != rec2.hour)
X	  return( rec1.hour - rec2.hour );
X
X	return( rec1.minute - rec2.minute );		/* ignore seconds... */
X}
X
Xint
Xmonth_number(name)
Xchar *name;
X{
X	/** return the month number given the month name... **/
X
X	char ch;
X
X	switch (tolower(name[0])) {
X	 case 'a' : if ((ch = tolower(name[1])) == 'p')	return(APRIL);
X		    else if (ch == 'u') return(AUGUST);
X		    else return(-1);	/* error! */
X	
X	 case 'd' : return(DECEMBER);
X	 case 'f' : return(FEBRUARY);
X	 case 'j' : if ((ch = tolower(name[1])) == 'a') return(JANUARY);
X		    else if (ch == 'u') {
X	              if ((ch = tolower(name[2])) == 'n') return(JUNE);
X		      else if (ch == 'l') return(JULY);
X		      else return(-1);		/* error! */
X	            }
X		    else return(-1);		/* error */
X	 case 'm' : if ((ch = tolower(name[2])) == 'r') return(MARCH);
X		    else if (ch == 'y') return(MAY);
X		    else return(-1);		/* error! */
X	 case 'n' : return(NOVEMBER);
X	 case 'o' : return(OCTOBER);
X	 case 's' : return(SEPTEMBER);
X	 default  : return(-1);
X	}
X}
X
X#ifdef SITE_HIDING
X
Xchar *get_ctime_date()
X{
X	/** returns a ctime() format date, but a few minutes in the 
X	    past...(more cunningness to implement hidden sites) **/
X
X	static char buffer[SLEN];	/* static character buffer       */
X	struct tm *the_time,		/* Time structure, see CTIME(3C) */
X		  *localtime();
X	long	   junk,		/* time in seconds....		 */
X		   time();
X#ifdef BSD
X	struct  timeval  time_val;		
X	struct  timezone time_zone;
X#endif
X
X#ifdef BSD
X	gettimeofday(&time_val, &time_zone);
X	junk = time_val.tv_sec;
X#else
X	junk = time((long *) 0);	/* this must be here for it to work! */
X#endif
X	the_time = localtime(&junk);
X
X	sprintf(buffer, "%s %s %d %02d:%02d:%02d %d",
X	  arpa_dayname[the_time->tm_wday],
X	  arpa_monname[the_time->tm_mon],
X	  the_time->tm_mday % 32,
X	  min(the_time->tm_hour % 24, (rand() % 24)),
X	  min(abs(the_time->tm_min  % 61 - (rand() % 60)), (rand() % 60)),
X	  min(abs(the_time->tm_sec  % 61 - (rand() % 60)), (rand() % 60)),
X	  the_time->tm_year % 100 + 1900);
X	
X	return( (char *) buffer);
X}
X
X#endif
END_OF_src/date.c
if test 10281 -ne `wc -c <src/date.c`; then
    echo shar: \"src/date.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"test/test.mail\" \(10003 characters\)
if test -f test/test.mail ; then 
  echo shar: Will not over-write existing file \"test/test.mail\"
else
sed "s/^X//" >test/test.mail <<'END_OF_test/test.mail'
XFrom root Wed Oct 30 14:03:36 1985
X>From srmmail Wed Oct 30 14:10:08 1985  remote from veeger
X>From hplabs Wed Oct 30 14:00:16 1985 remote from hpcnof
X>From hpl-opus!poulton  Wed Oct 30 02:06:16 1985 remote from hplabs
XDate: Wed, 30 Oct 85 01:55:05 pst
XFrom: <hplabs!hpl-opus!poulton>
XReceived: by HP-VENUS id AA26352; Wed, 30 Oct 85 01:55:05 pst
XMessage-Id: <8510300955.AA26352@HP-VENUS>
XTo: hplabs!hpldat!taylor
XSubject: Re: announce(1)
X
XThe announce I got was shar'd July 8.   NLEN was not defined in that
Xsource, just used.  LONG_SLEN is not defined in the newmail(1)
Xthat you sent me.  What system are you running on?
XMy s500 doesn't have these def's.
X
X	-> Monday, January 3rd: Call your mother
X
XAs to announce --> newmail: why the switch?
XSeems like both are useful, in different situations.
X
XKen Poulton
XHPL
X
X
X
X
XFrom root Wed Oct 30 14:03:39 1985
X>From srmmail Wed Oct 30 14:10:12 1985  remote from veeger
X>From hplabs Wed Oct 30 13:59:53 1985 remote from hpcnof
X>From fowler  Wed Oct 30 12:57:11 1985 remote from hplabs
XDate: Wed, 30 Oct 85 12:57:11 pst
XFrom: Greg Fowler <hplabs!fowler>
XReceived: by HP-VENUS id AA12562; Wed, 30 Oct 85 12:57:11 pst
XMessage-Id: <8510302057.AA12562@HP-VENUS>
XTo: mail-men@rochester
XSubject: Re: Summary of Network Mail Headers
XReferences: <36700044@hpcnof.UUCP>
XPriority: Most Urgent
X
XI believe your introduction referred to the uucp network.  usenet is the networXk news
Xsoftware mechanism and isn't a "network".
X
X	- > February 19, 1986
X	-
X	-    A longer test of the system
X	-
X
X	Greg
X
X
X
XFrom root Wed Oct 30 14:13:23 1985
X>From srmmail Wed Oct 30 14:20:08 1985  remote from veeger
X>From root Wed Oct 30 14:01:57 1985 remote from hpcnof
XTo: DCC@hplabs
XSubject: Log of backup tape #1
X
X 
XFull Backup starting at Wed Oct 30 12:45:14 MST 1985
X 
X
X
Xbacking up directories: 
X	./users/fh ./users/rmd ./users/vince ./users/roberts ./users/row ./users/dt ./Xlost+found ./users/lost+found ./users/scb ./users/kevin ./users/du
X
X
X
X
X
XFrom root Wed Oct 30 15:33:24 1985
X>From srmmail Wed Oct 30 15:40:26 1985  remote from veeger
X>From root Wed Oct 30 15:37:17 1985 remote from hpcnof
XTo: root, uucp, taylor@hplabs.ARPA
XSubject: Log of backup tape #2
X
Xbacking up directories: 
X	./users/fh ./users/rmd ./users/vince ./users/roberts ./users/row ./users/dt ./Xlost+found ./users/lost+found ./users/scb ./users/kevin ./users/du
X
X
X
X
Xbacking up directories: 
X	./users/sbh ./users/ges ./users/cpb ./users/amy ./net ./users/root ./users/balXza ./dev ./users/remple ./users/jr ./users/mwr ./users/larryf
X
X
X
X
X
XFrom root Sun Dec  8 22:50:18 1985
X>From srmmail Mon Dec  9 00:50:05 1985 remote from veeger
X>From root Mon Dec  9 00:41:15 1985 remote from hpcnof
X>From JLarson.pa@Xerox.ARPA  Sun Dec  8 20:45:55 1985 remote from hplabs
XDate: 8 Dec 85 20:36:36 PST (Sunday)
XFrom: hplabs!JLarson.pa@Xerox.ARPA
XSubject: How's it going, anyway?
XTo: hpcnou!dat@HPLABS.ARPA (Dave Taylor)
XCc: JLarson.pa@Xerox.ARPA
X
XHow are things with you?  Could you send me that paper we were talking
Xabout?  
X
X	Thanks
X
XJohn Larson
XXerox Palo Alto Research Center
X3333 Coyote Hill Road
XPalo Alto, Ca  94304
X
X
X
X
XFrom root Wed Aug  7 19:58:30 1985
X>From uucp Wed Aug  7 19:55:12 1985  remote from veeger
X>From hplabs Wed Aug  7 19:48:10 1985 remote from hpcnof
X>From RICHER@SUMEX-AIM  Wed Aug  7 09:23:12 1985 remote from hplabs
XReceived: by HP-VENUS id AA18269; Wed, 7 Aug 85 09:11:48 pdt
XDate: Tue 6 Aug 85 09:12:37-PDT
XFrom: Mark Richer <hplabs!RICHER@SUMEX-AIM>
XReceived: by HP-VENUS via CSNET; 7 Aug 1985 09:11:37-PDT (Wed)
XReceived: from sumex-aim.arpa by csnet-relay.arpa id a015812; 6 Aug 85 12:14 EDXT
XTo: hpcnof!veeger!hpcnou!dat%hplabs.csnet@CSNET-RELAY
XVia:  CSNet; 7 Aug 85 9:11-PDT
XSubject: Re: AI in Education mailing list...
XCc: RICHER@SUMEX-AIM
XIn-Reply-To: <8508030243.AA27641@HP-VENUS>
XMessage-Id: <12132987812.61.RICHER@SUMEX-AIM.ARPA>
X
XI added you to aied.  This message may be of interest to you:
X
XArtificial Intelligence in Education Meeting at IJCAI 85
X---------- ------------ -- --------- ------- -- ----- --
X
XPlace: Math Sciences Auditorium (a.k.a. Math 4000A), UCLA campus
XTime: 6:30 pm, Tuesday, Aug. 20, 1985  (length: 1 - 1 1/4 hr)
X
XAgenda:
X	I have two speakers scheduled to make presentations that
Xshould stimulate questions and discussions:
X
X	(1) Short Announcements
X
X	(2) Jeff Bonar, Research Scientist, Learning Research and
X	Development Center (LRDC), University of Pittsburgh
X
X	--- on-going ICAI research projects at LRDC
X	--- dissemination of ICAI technology in the form of software
X	tools, workshops, written materials, and video tapes.
X
X	(3) Gary Fine, Product Engineering Manager, INTELLICORP,
X	formerly with a company producing CAI products, also graduate
X	work in ICAI  
X
X	--- bridging the gap between current ICAI technology and the
X	real world
X
X[IJCAI-85, the 9th International Joint Conference on Artificial
XIntelligence is being held at UCLA Campus, August 18-23, 1985.  This
Xconference is co-sponsered by the American Association for Artificial
XIntelligence (AAAI) this year, and I have been told by their office
Xthat only walk-in registration is available at this time.  For more
Xinformation, contact AAAI:  AAAI-OFFICE@SUMEX-AIM.ARPA
X			    AAAI, 445 Burgess Drive, Menlo Park, CA 94025
X			    or call (415) 328-3123]
X
XDirect questions on the AI in ED meeting (only) to Mark Richer,
XRICHER@SUMEX-AIM.ARPA
X-------
X
X
X
X
XFrom root Tue Sep 24 09:53:24 1985
X>From HPMAIL-gateway Tue Sep 24  9:46:47 1985  remote from veeger
X>From Simon_CINTZ_/_HPD600/TR  Tue Sep 24  9:46:47 1985  remote from hpmail
XDate:   Tue, 24 Sep 85  9:14:00 MDT
XFrom:   Simon_CINTZ_/_HPD600/TR  (Simon Cintz)
XSubject: ITF
XFrom:   Simon_CINTZ_/_HPD600/TR  (Simon Cintz)
XTo:     Dave_TAYLOR_/_HPF100/00
X
XDave -
X
XJust as one programming language doesn't suit the needs of
Xall programmers, one authoring facility will probably not
Xsuit the needs of all HP entities that require CBT -- at least
Xnot in the near future.  Of course, this is my personal opinion
Xand if I'm wrong, it won't be the first time.
X
XGood luck.
X
X
X                                           - Simon
X
XFrom root Mon Oct 21 10:43:37 1985
X>From srmmail Mon Oct 21 10:30:16 1985  remote from veeger
X>From root Mon Oct 21 10:28:58 1985 remote from hpcnof
X>From DLS.MDC%office-X.arpa@CSNET-RELAY  Mon Oct 21 01:57:05 1985 remote from hXplabs
XReceived: by HP-VENUS id AA17376; Mon, 21 Oct 85 01:57:05 pdt
XDate: 21 Oct 85 01:02 EDT
XFrom: Duane Stone / McDonnell Douglas / CSC-ASD <hplabs!DLS.MDC%office-1.arpa@CXSNET-RELAY>
XReceived: by HP-VENUS via CSNET; 21 Oct 1985 01:57:01-PDT (Mon)
XReceived: from office-1.arpa by CSNET-RELAY.ARPA id a019220; 21 Oct 85 1:18 EDTX
XTo: Dave Taylor <hpcnou!dat%hplabs.csnet@CSNET-RELAY>
XVia:  CSNet; 21 Oct 85 1:56-PDT
XSubject: Re: More Mail Headers...
XMessage-Id: <MDC-DLS-7W9CS@OFFICE-1>
XComment: Dave -- this is the body of the message I previously 'sent' to you viaX
X
Xa Journal.
X
XI might suggest re-wording the para on Author -- my associates might object to X
X'strange' -- something like:
X
X   This is used to credit the original author, or to give credit on article 
X   excerpts (from Newspapers, magazines, books, etc).
X
XOne field which I forgot is:
X
X   Length:  This is computed when the message is sent and gives the recipients X
X   an estimate of the number of pages in the document.
X
X   Example:
X
X      Length: 6 pages [estimate]
X
XAccess:
X
X   Used to declare whether a Journal item should be Public or Private (to thoseX
X   that are on the distribution list or Extended Access list)
X
X   Example:
X
X      Access: Unrestricted
X
XAcknowledge-Delivery:
X
X   Used to request the system mailer send back a message when it has 
X   successfully delivered the item.
X
X   Example:
X
X      Acknowledge-Delivery: Requested
X
XAcknowledge-Receipt:
X
X   Used to to ask the recipient to acknowledge receipt of the message.
X
X   Example:
X
X   Acknowledge-Receipt: Requested
X
XAddendum-To:
X
X   A pointer to a previously submitted Journal item.
X
X   Example:
X
X      Addendum-To: <ASD,1234,>
X
XDelivery-Timing:
X
X   Used by the sender to indicate when the message should be submitted to the 
X   mailer.
X
X      Examples:
X
X         Rush:       -   immediate
X
X         Soon:       -   as soon as possible
X
X         Defer:      -   overnight
X
X         Start-Delivery: DATE TIME
X
X         Stop-Delivery:  DATE TIME (if not yet delivered)
X
XDisposition-Code:
X
X   Used by the system to group Journal items into one of several classes for 
X   eventual archive to tape and as an indicator of how long the archive tapes 
X   should be retained.
X
X   Example:
X
X      Disposition-Code: Temporary (2 years)
X
XExtended-access:
X
X   Used with private Journal items to allow access by other than those on the 
X   distribution list.
X
X   Example:
X
X      Extended-access: ASD.MDC
X
XLocation:
X
X   Used to submit the message to the Journal.  The adressees receive a short 
X   citation with other header fields and a "Location:" field pointing to a fileX
X   in an electronic library.
X
X   Example:
X
X      Location: <MDC,1234,>
X
XPart-Of:
X
X   A pointer to a previously submitted Journal item.
X
X   Example:
X
X      Part-Of: <MDC,1234,>
X
XRoute-To:
X
X   Used to send a message "in-turn" to addressees in the "To:" field -- as 
X   opposed to the broadcast method of delivery where everyone gets the message X
X   "simultaneously".  Any addresses in the "Cc:" field receive a copy of the 
X   message each time it is passed from one adressee to the next in the "To:" 
X   field.
X
X   Example:
X
X      Routed-to: {addresses in To field}
X
XSigned:
X
X   Created when the user employs the Sign command; used to electronically sign X
X   a message.  It affixes a signature-block to a message.  A "Verify Signature"X
X   command is available to recipients that lets them find out if anyone has 
X   changed the body of the message since the message was signed.
X
X   Example:
X
X          SIGNED
X      
X      Duane L. Stone
X      App. Dev. Mgr.
X
XSupersedes:
X
X   A pointer to a previously submitted Journal item.
X
X   Example:
X
X      Supersedes: <MDC,1234,>
X
X
X--- last line of the file --
END_OF_test/test.mail
if test 10003 -ne `wc -c <test/test.mail`; then
    echo shar: \"test/test.mail\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"utils/from.c\" \(9633 characters\)
if test -f utils/from.c ; then 
  echo shar: Will not over-write existing file \"utils/from.c\"
else
sed "s/^X//" >utils/from.c <<'END_OF_utils/from.c'
X/**		from.c		**/
X
X/** print out whom each message is from in the pending mailbox
X    or specified one, including a subject line if available.. 
X
X    Added PREFER_UUCP knowledge 6/86
X    Added "-n" flag, 9/86
X
X    (C) Copyright 1986 Dave Taylor 
X**/
X
X#include <stdio.h>
X#include "defs.h"
X
Xstatic char ident[] = { WHAT_STRING };
X
X#define LINEFEED	(char) 10
X
X#define metachar(c)	(c == '=' || c == '+' || c == '%')
X
XFILE *mailfile;
X
Xchar *expand_define();
Xint   number = 0;	/* should we number the messages?? */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	char infile[LONG_SLEN], username[SLEN];
X
X	if (argc > 2) {
X	  printf("Usage: %s [-n] {filename | username}\n", argv[0]);
X	  exit(1);
X	}
X
X	if (argc > 1)
X	  if (strcmp(argv[1], "-n") == 0) {
X	    number++;
X	    *argv++;
X	    argc--;
X	  }
X
X	if (argc == 2) 
X	  strcpy(infile, argv[1]);
X	else {
X	  strcpy(username, getlogin());
X	  if (strlen(username) == 0)
X	    cuserid(username);
X	  sprintf(infile,"%s%s",mailhome, username);
X	}
X
X	if (metachar(infile[0])) {
X	  if (expand(infile) == 0) {
X	     fprintf(stderr, "%s: couldn't expand filename %s!\n", 
X		     argv[0], infile);
X	     exit(1);
X	  }
X	}
X	else
X	  printf("filename '%s' doesn't have a metacharacter!\n", infile);
X
X	if ((mailfile = fopen(infile,"r")) == NULL) {
X	  if (argc == 1)
X	    printf("No mail!\n");
X	  else {
X	    if (argv[1][0] == '/') 
X	      printf("Couldn't open folder/mailfile %s!\n", infile);
X	    else {
X	      sprintf(infile,"%s%s", mailhome, argv[1]);
X	      if ((mailfile = fopen(infile,"r")) == NULL)
X	        printf("Couldn't open folder/mailfile %s!\n", infile);
X	      else if (read_headers()==0)
X	        printf("No messages in mailbox!\n");
X	     }
X	  }
X	}
X	else
X	  if (read_headers()==0)
X	    printf("No messages in mailbox!\n");
X}
X
Xint
Xread_headers()
X{
X	/** read the headers, output as found **/
X
X	char buffer[LONG_SLEN], from_whom[SLEN], subject[SLEN];
X	register int subj = 0, in_header = 1, count = 0;
X
X	while (fgets(buffer, LONG_SLEN, mailfile) != NULL) {
X	  if (first_word(buffer,"From ")) {
X	    if (real_from(buffer, from_whom)) {
X	      subj = 0;
X	      in_header = 1;
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,"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(count+1, from_whom, subject);
X	      from_whom[0] = 0;
X	      subject[0] = 0;
X	      count++;
X	    }
X	  }
X	}
X	return(count);
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[80];
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[80], buff[80];
X
X	machine[0] = '\0';
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, 80);
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(count, from, subject)
Xint  count;
Xchar *from, *subject;
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
X	char buffer[SLEN];
X	int  loc, i=0, exc=0;
X
X#ifdef PREFER_UUCP
X	
X	if (chloc(from,'!') != -1 && in_string(from, BOGUS_INTERNET))
X	  from[strlen(from) - strlen(BOGUS_INTERNET)] = '\0';
X
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	  if (number)
X	    printf("%3d: %-20s  %s\n", count, buffer, subject);
X	  else
X	    printf("%-20s  %s\n", buffer, subject);
X	}
X	else
X	  if (number)
X	    printf("%3d: %-20s  %s\n", count, from, subject);
X	  else
X	    printf("%-20s  %s\n", 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;
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	  for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '(' &&
X	       buffer[i] != '<'; i--)
X	    temp[j++] = buffer[i];
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
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 strcpy(buffer, maildir);
X
X	return( ( char *) buffer);
X}
END_OF_utils/from.c
if test 9633 -ne `wc -c <utils/from.c`; then
    echo shar: \"utils/from.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: End of archive 10 \(of 19\).
cp /dev/null ark10isdone
DONE=true
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	echo shar: You still need to run archive ${I}.
	DONE=false
    fi
done
if test "$DONE" = "true" ; then
	echo You have unpacked all 19 archives.
	echo "See the Instructions file"
	rm -f ark[1-9]isdone ark[1-9][0-9]isdone
fi
##  End of shell archive.
exit 0