[comp.mail.elm] Elm 2.1 PL1 Part 12 of 22

syd@dsinc.UUCP (Syd Weinstein) (12/12/88)

---- Cut Here and unpack ----
#!/bin/sh
# this is part 12 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file src/elm.c continued
#
CurArch=12
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 src/elm.c"
sed 's/^X//' << 'SHAR_EOF' >> src/elm.c
X		(header_table[current-1].status & VISIBLE) != 0);
X
X	Write_to_screen(buffer, 0);
X
X	sprintf(buffer, "\nReceived on: %d/%d/%d at %d:%02d\n",
X	        header_table[current-1].received.month+1,
X	        header_table[current-1].received.day,
X	        header_table[current-1].received.year,
X	        header_table[current-1].received.hour,
X	        header_table[current-1].received.minute);
X	Write_to_screen(buffer, 0);
X
X	sprintf(buffer, "Message sent on: %s, %s %s, %s at %s\n",
X	        header_table[current-1].dayname,
X	        header_table[current-1].month,
X	        header_table[current-1].day,
X	        header_table[current-1].year,
X	        header_table[current-1].time);
X	Write_to_screen(buffer, 0);
X	
X	Write_to_screen("From: %s\nSubject: %s", 2,
X		header_table[current-1].from,
X	        header_table[current-1].subject);
X
X	Write_to_screen("\nPrimary Recipient: %s\nInternal Index Reference Number = %d\n", 2,
X		header_table[current-1].to,
X		header_table[current-1].index_number);
X
X	Write_to_screen("Message-ID: %s\n", 1,
X		strlen(header_table[current-1].messageid) > 0 ? 
X		header_table[current-1].messageid : "<none>");
X	
X	Raw(ON);
X
X	PutLine0(LINES,0,"Please Press any key to return: ");
X	(void) ReadCh();
X}
SHAR_EOF
echo "File src/elm.c is complete"
chmod 0444 src/elm.c || echo "restore of src/elm.c fails"
echo "x - extracting src/encode.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/encode.c &&
X
Xstatic char rcsid[] = "@(#)$Id: encode.c,v 2.1 88/09/15 20:28:08 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	encode.c,v $
X * Revision 2.1  88/09/15  20:28:08  syd
X * checked in with -k by syd at 88.09.15.20.28.08.
X * 
X * Revision 2.1  88/07/21  09:58:21  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:00  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** This is a heavily mangled version of the 'cypher' program written by
X    person or persons unknown.  
X
X**/
X
X#include <stdio.h>
X#include "curses.h"
X#include "headers.h"
X
X#define RS	94
X#define RN	4
X#define RMASK	0x7fff	/* use only 15 bits */
X
Xstatic char r[RS][RN];		/* rotors */
Xstatic char ir[RS][RN];		/* inverse rotors */
Xstatic char h[RS];		/* half rotor */
Xstatic char s[RS];		/* shuffle vector */
Xstatic int  p[RN];		/* rotor indices */
X
Xstatic char the_key[SLEN];	/* unencrypted key */
Xstatic char *encrypted_key;	/* encrypted key   */
X
Xchar *getpass(), *strncpy(), *strcpy();
Xunsigned long sleep();
X
Xgetkey(send)
Xint send;
X{
X	/** this routine prompts for and returns an encode/decode
X	    key for use in the rest of the program.  If send == 1
X	    then need to mess with rawmode. **/
X
X	char buffer[NLEN];
X	int gotkey = 0, x, y;
X
X	GetXYLocation(&x,&y);
X
X	ClearLine(21);
X
X	if (send) Raw(OFF);
X
X	while ( !gotkey ) {
X	  MoveCursor(LINES-1,0);
X	  ClearLine(LINES-1);
X	  if (send)
X	    strcpy( buffer, getpass( "Enter encryption key: "));
X	  else
X	    strcpy( buffer, getpass( "Enter decryption key: "));
X	  MoveCursor(LINES-1,0);
X	  if ( send && strcmp( buffer, getpass( "Please enter it again: "))) {
X	    error("Your keys were not the same!");
X	    sleep(1);
X	    clear_error();
X	    continue;
X	  }
X	  strcpy(the_key, buffer);	/* save unencrypted key */
X	  makekey( buffer );
X	  gotkey = 1;
X	}
X
X	if (send) Raw(ON);
X
X	setup();		/** initialize the rotors etc. **/
X
X	ClearLine(LINES-1);		
X	clear_error();
X
X	MoveCursor(x+1,0);	/* move to 'next' line... */
X}
X
Xget_key_no_prompt()
X{
X	/** This performs the same action as get_key, but assumes that
X	    the current value of 'the_key' is acceptable.  This is used
X	    when a message is encrypted twice... **/
X
X	char buffer[SLEN];
X
X	strcpy(buffer, the_key);
X
X	makekey( buffer );
X
X	setup();
X}
X
Xencode(line)
Xchar *line;
X{
X	/** encrypt or decrypt the specified line.  Uses the previously
X	    entered key... **/
X
X	register int i, index, j, ph = 0;
X
X	for (index=0; index < strlen(line); index++) {
X	  i = (int) line[index];
X
X	  if ( (i >= ' ') && (i < '~') ) {
X	    i -= ' ';
X
X	    for ( j = 0; j < RN; j++ )		/* rotor forwards */
X	      i = r[(i+p[j])%RS][j];
X
X	    i = ((h[(i+ph)%RS])-ph+RS)%RS;	/* half rotor */
X
X	    for ( j--  ; j >= 0; j-- )		/* rotor backwards */
X	      i = (ir[i][j]+RS-p[j])%RS;
X
X	    j = 0;				/* rotate rotors */
X	    p[0]++;
X	    while ( p[j] == RS ) {
X	      p[j] = 0;
X	      j++;
X	      if ( j == RN ) break;
X	      p[j]++;
X            }
X  
X	    if ( ++ph == RS )
X	      ph = 0;
X
X	    i += ' ';
X	  }
X	  
X	  line[index] = (char) i;	/* replace with altered one */
X	}
X}
X
X
Xmakekey( rkey)
Xchar *rkey;
X{
X	/** encrypt the key using the system routine 'crypt' **/
X
X	char key[8], salt[2], *crypt();
X
X	strncpy( key, rkey, 8);
X	salt[0] = key[0];
X	salt[1] = key[1];
X	encrypted_key = crypt( key, salt);
X}
X
X/*
X * shuffle rotors.
X * shuffle each of the rotors indiscriminately.  shuffle the half-rotor
X * using a special obvious and not very tricky algorithm which is not as
X * sophisticated as the one in crypt(1) and Oh God, I'm so depressed.
X * After all this is done build the inverses of the rotors.
X */
X
Xsetup()
X{
X	register long i, j, k, temp;
X	long seed;
X
X	for ( j = 0; j < RN; j++ ) {
X		p[j] = 0;
X		for ( i = 0; i < RS; i++ )
X			r[i][j] = i;
X	}
X
X	seed = 123;
X	for ( i = 0; i < 13; i++)		/* now personalize the seed */
X	  seed = (seed*encrypted_key[i] + i) & RMASK;
X
X	for ( i = 0; i < RS; i++ )		/* initialize shuffle vector */
X	  h[i] = s[i] = i;
X
X	for ( i = 0; i < RS; i++) {		/* shuffle the vector */
X	  seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
X	  k = ((seed % 65521) & RMASK) % RS;
X	  temp = s[k];
X	  s[k] = s[i];
X	  s[i] = temp;
X	}
X
X	for ( i = 0; i < RS; i += 2 ) {	/* scramble the half-rotor */
X	  temp = h[s[i]];			/* swap rotor elements ONCE */
X	  h[s[i]] = h[s[i+1]];
X	  h[s[i+1]] = temp;
X	}
X
X	for ( j = 0; j < RN; j++) {			/* select a rotor */
X
X	  for ( i = 0; i < RS; i++) {		/* shuffle the vector */
X	    seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
X	    k = ((seed % 65521) & RMASK) % RS;
X	    temp = r[i][j];
X	    r[i][j] = r[k][j];
X	    r[k][j] = temp;
X	  }
X
X	  for ( i = 0; i < RS; i++) 		/* create inverse rotors */
X	    ir[r[i][j]][j] = i;
X       }
X}
SHAR_EOF
chmod 0444 src/encode.c || echo "restore of src/encode.c fails"
echo "x - extracting src/errno.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/errno.c &&
X
Xstatic char rcsid[] = "@(#)$Id: errno.c,v 2.1 88/09/15 20:28:11 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	errno.c,v $
X * Revision 2.1  88/09/15  20:28:11  syd
X * checked in with -k by syd at 88.09.15.20.28.11.
X * 
X * Revision 2.1  88/07/21  09:58:24  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:01  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** This routine maps error numbers to error names and error messages.
X    These are all directly ripped out of the include file errno.h, and
X    are HOPEFULLY standardized across the different breeds of Unix!!
X
X    If (alas) yours are different, you should be able to use awk to
X    mangle your errno.h file quite simply...
X
X**/
X
X#include "headers.h"
X
Xchar *err_name[] = { 
X/* 0 */	        "NOERROR", "No error status currently",
X/* 1 */		"EPERM",   "Not super-user",
X/* 2 */		"ENOENT",  "No such file or directory",
X/* 3 */		"ESRCH",   "No such process",
X/* 4 */		"EINTR",   "Interrupted system call",
X/* 5 */		"EIO",     "I/O error",
X/* 6 */		"ENXIO",   "No such device or address",
X/* 7 */		"E2BIG",   "Arg list too long",
X/* 8 */		"ENOEXEC", "Exec format error",
X/* 9 */		"EBADF",   "Bad file number",
X/* 10 */	"ECHILD",  "No children",
X/* 11 */	"EAGAIN",  "No more processes",
X/* 12 */	"ENOMEM",  "Not enough core",
X/* 13 */	"EACCES",  "Permission denied",
X/* 14 */	"EFAULT",  "Bad address",
X/* 15 */	"ENOTBLK", "Block device required",
X/* 16 */	"EBUSY",   "Mount device busy",
X/* 17 */	"EEXIST",  "File exists",
X/* 18 */	"EXDEV",   "Cross-device link",
X/* 19 */	"ENODEV",  "No such device",
X/* 20 */	"ENOTDIR", "Not a directory",
X/* 21 */	"EISDIR",  "Is a directory",
X/* 22 */	"EINVAL",  "Invalid argument",
X/* 23 */	"ENFILE",  "File table overflow",
X/* 24 */	"EMFILE",  "Too many open files",
X/* 25 */	"ENOTTY",  "Not a typewriter",
X/* 26 */	"ETXTBSY", "Text file busy",
X/* 27 */	"EFBIG",   "File too large",
X/* 28 */	"ENOSPC",  "No space left on device",
X/* 29 */	"ESPIPE",  "Illegal seek",
X/* 30 */	"EROFS",   "Read only file system",
X/* 31 */	"EMLINK",  "Too many links",
X/* 32 */	"EPIPE",   "Broken pipe",
X/* 33 */	"EDOM",    "Math arg out of domain of func",
X/* 34 */	"ERANGE",  "Math result not representable",
X/* 35 */	"ENOMSG",  "No message of desired type",
X/* 36 */	"EIDRM",   "Identifier removed"
X	};
X
Xchar *strcpy();
X
Xchar *error_name(errnumber)
Xint errnumber;
X{
X	static char buffer[50];
X
X	if (errnumber < 0 || errnumber > 36) 
X	  sprintf(buffer,"ERR-UNKNOWN (%d)", errnumber);
X	else
X	  strcpy(buffer, err_name[2*errnumber]);
X
X	return( (char *) buffer);
X}
X
Xchar *error_description(errnumber)
Xint errnumber;
X{
X	static char buffer[50];
X
X	if (errnumber < 0 || errnumber > 36) 
X	  sprintf(buffer,"Unknown error - %d - No description", errnumber);
X	else
X	  strcpy(buffer, err_name[2*errnumber + 1]);
X
X	return ( (char *) buffer);
X}
SHAR_EOF
chmod 0444 src/errno.c || echo "restore of src/errno.c fails"
echo "x - extracting src/expires.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/expires.c &&
X
Xstatic char rcsid[] = "@(#)$Id: expires.c,v 2.1 88/09/15 20:28:13 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1985 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	expires.c,v $
X * Revision 2.1  88/09/15  20:28:13  syd
X * checked in with -k by syd at 88.09.15.20.28.13.
X * 
X * 88/08/27  Rob Bernardo <gatech!pbhyf.PacBell.COM!rob>
X *	fix date parsing
X *
X * Revision 2.1  88/07/21  09:58:25  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:02  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** This routine is written to deal with the Expires: header on the
X    individual mail coming in.  What it does is to look at the date,
X    compare it to todays date, then set the EXPIRED flag on the
X    current message if it is true...
X**/
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
Xchar *shift_lower(), *strcpy(), *strncpy();
X
Xprocess_expiration_date(date, message_status)
Xchar *date;
Xint  *message_status;
X{
X	struct tm *timestruct, *localtime();
X	long *thetime, *time();
X	char word1[NLEN], word2[NLEN], word3[NLEN], word4[NLEN];
X	int  month = 0, day = 0, year = 0, hour = 0, minute = 0;
X
X	/** first step is to break down the date given into MM DD YY HH MM
X	    format:  The possible formats for this field are, by example:
X	
X		(1) Mon, Jun 11, 87
X		(2) Mon, 11 Jun 87
X		(3) Jun 11, 87
X		(4) 11 Jun 87
X		(5) 11/06/87	<- ambiguous - will be ignored!!
X
X	    The reason #5 is considered ambiguous will be made clear
X	    if we consider a message to be expired on Jan 4, 88:
X			01/04/88	in the United States
X			04/01/88	in Europe
X	    so is the first field the month or the day?  Standard prob.
X	**/
X
X	sscanf(date, "%s %s %s %s", word1, word2, word3, word4);
X
X	if (strlen(word2) == 0) {	/* we have form #6 or form #5 */
X	  if (isdigit(word2[1]) && isdigit(word2[2]))	  /* form #6! */
X	    sscanf(word1, "%02d%02d%02d%02d%02d%*c",
X		 &year, &month, &day, &hour, &minute);
X	}
X	else if (strlen(word4) != 0) {		   /* form #1 or form #2 */
X	  if(isdigit(word2[0])) {		   /* form #2 */
X	      month = month_number(word3);
X	      day   = atoi(word2);
X	      year  = atoi(word4);
X	  } else {				   /* form #1 */
X	      month = month_number(word2);
X	      day   = atoi(word3);
X	      year  = atoi(word4);
X	  }
X	}
X	else if (! isdigit(word1[0])) {		   /* form #3 */
X	  month = month_number(word1);
X	  day   = atoi(word2);
X	  year  = atoi(word3);
X	}
X	else {					   /* form #4 */
X	  day   = atoi(word2);
X	  month = month_number(word3);
X	  year  = atoi(word4);
X	}
X
X	if (day == 0 || year == 0)
X	  return;			/* we didn't get a valid date */
X
X	/** next let's get the current time and date, please **/
X
X	thetime = time((long *) 0);
X
X	timestruct = localtime(&thetime);
X
X	/** and compare 'em **/
X
X	if (year > timestruct->tm_year)
X	  return;
X	else if (year < timestruct->tm_year)
X	  goto expire_message;
X
X	if (month > timestruct->tm_mon)
X	  return;
X	else if (month < timestruct->tm_mon)
X	  goto expire_message;
X
X	if (day > timestruct->tm_mday)
X	  return;
X	else if (day < timestruct->tm_mday)
X	  goto expire_message;
X
X	if (hour > timestruct->tm_hour)
X	  return;
X	else if (hour < timestruct->tm_hour)
X	  goto expire_message;
X
X	if (minute > timestruct->tm_min)
X	  return;
X
Xexpire_message:
X
X	/** it's EXPIRED!  Yow!! **/
X
X	(*message_status) |= EXPIRED;
X}
SHAR_EOF
chmod 0444 src/expires.c || echo "restore of src/expires.c fails"
echo "x - extracting src/file.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/file.c &&
X
Xstatic char rcsid[] = "@(#)$Id: file.c,v 2.1 88/09/15 20:28:17 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	file.c,v $
X * Revision 2.1  88/09/15  20:28:17  syd
X * checked in with -k by syd at 88.09.15.20.28.17.
X * 
X * 88/09/13 Rob Bernardo <gatech!pbhyf.PacBell.COM!rob>
X * The message that appears if you try to save a mail message to a
X * file that exists that cannot be opened for appending mentioned a
X * permissions problem, but since the  problem can be other than one
X * of permissions (e.g. trying to save to a directory instead of a
X * regular file), I have made the message more general.
X *
X * 88/08/27 Rob Bernardo <gatech!pbhyf.PacBell.COM!rob>
X *	fix saving to/ apending to messages
X *
X * Revision 2.1  88/07/21  09:58:27  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:03  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** File I/O routines, mostly the save to file command...
X
X**/
X
X#include "headers.h"
X#include <ctype.h>
X#include <errno.h>
X
X#ifdef BSD
X#undef tolower
X#endif
X
Xextern int errno;
X
Xchar *error_name(), *error_description(), *strcpy(), *getenv();
Xunsigned long sleep();
X
Xint
Xsave(redraw, silently)
Xint *redraw, silently;
X{
X	/** Save all tagged messages + current in a file.  If no messages
X	    are tagged, save the current message instead!  This routine
X	    will return ZERO if the operation failed.
X	    'redraw' is set to TRUE iff we use the '?' and mess up
X	    the screen.  Pretty reasonable, eh?  If "silently" is set,
X	    then don't output the "D" character upon marking for
X	    deletion...
X	**/
X
X	register int tagged = 0, i, oldstat, appending = 0;
X	register int iterations = 0, continue_looping;
X	char filename[SLEN], address[LONG_SLEN], buffer[SLEN];
X	FILE *save_file;
X
X	oldstat = header_table[current-1].status;	/* remember */
X
X	for (i=0; i < message_count; i++)
X	  if (ison(header_table[i].status, TAGGED))
X	    tagged++;
X
X	if (tagged == 0) {
X	  tagged = 1;
X	  setit(header_table[current-1].status, TAGGED);
X	}
X
X	dprint(4, (debugfile, "%d message%s tagged for saving (save)\n", tagged,
X		plural(tagged)));
X
X	do {
X
X	  continue_looping = 0;	/* clear the flag, ho hum... */
X
X	  if (iterations++) {
X	    printf("File message%s in: ", plural(tagged));
X	    fflush(stdout);
X	  }
X	  else
X	    PutLine1(LINES-2, 0, "File message%s in: ", plural(tagged));
X
X	  if (save_by_name) {
X	    /** build default filename to save to **/
X	    get_return(address);
X	    get_return_name(address, buffer, TRUE);
X	    sprintf(filename, "=%s", buffer);
X	  }
X	  else
X	    filename[0] = '\0';
X
X	  if (iterations > 1) {
X	    optionally_enter(filename, -1, -1, FALSE);
X	  } 
X	  else {
X	    if (tagged > 1)
X	      optionally_enter(filename, LINES-2, 19, FALSE);
X	    else	
X	      optionally_enter(filename, LINES-2, 18, FALSE);
X	  }
X  
X	  if (iterations == 1)
X	    MoveCursor(LINES-1,0);
X
X	  if (strlen(filename) == 0) {  /** <return> means 'cancel', right? **/
X	    header_table[current-1].status = oldstat;	/* BACK! */
X	    return(0);
X	  }
X	 
X	  if (strcmp(filename,"?") == 0) {
X	    *redraw = TRUE;	/* set the flag so we know what to do later */
X	    Raw(OFF);
X	    list_folders();
X	    Raw(ON);
X	    continue_looping++;
X	  }
X	} while (continue_looping);
X
X	if (! expand_filename(filename)) {
X	  dprint(2, (debugfile,
X		"Error: Failed on expansion of filename %s (save)\n", 
X		filename));
X	  header_table[current-1].status = oldstat;	/* BACK! */
X	  return(0);	/* failed expanding name! */
X	}
X
X	if (access(filename,ACCESS_EXISTS)== 0) 	/* already there!! */
X	  appending = 1;
X	  
X#ifdef BSD4_1
X	if ((errno = ((can_open(filename, "a") & ~0x0200) >>8))) {
X#else
X	if ((errno = can_open(filename, "a"))) {
X#endif
X	  error1("Cannot save message to file %s!", filename);
X	  dprint(2, (debugfile, 
X		"Error: access permission on file %s denied (%s)! (save)\n",
X		filename, error_name(errno)));
X	  header_table[current-1].status = oldstat;	/* BACK! */
X	  return(0);
X	}
X
X	dprint(4,(debugfile, "Saving mail to file '%s'...\n", filename));
X
X	if ((save_file = fopen(filename,"a")) == NULL) {
X	  dprint(2, (debugfile,
X		"Error: couldn't append to specified file %s (save)\n", 
X		filename));
X	  error1("Couldn't append to file %s!", filename);
X	  header_table[current-1].status = oldstat;	/* BACK! */
X	  return(0); 
X	}
X
X	for (i=0; i < message_count; i++) 	/* save each tagged msg */
X	  if (header_table[i].status & TAGGED)
X	    save_message(i, filename, save_file, (tagged > 1), appending++, 
X			 silently);
X
X	fclose(save_file);
X
X	chown(filename, userid, groupid);	/* owned by user */
X	if (tagged > 1)
X	  error1("Message%s saved", plural(tagged));
X	return(1);
X}
X
Xint
Xsave_message(number, filename, fd, pause, appending, silently)
Xint number, pause, appending, silently;
Xchar *filename;
XFILE *fd;
X{
X	/** Save an actual message to a file.  This is called by 
X	    "save()" only!  The parameters are the message number,
X	    and the name and file descriptor of the file to save to.
X	    If 'pause' is true, a sleep(2) will be done after the
X	    saved message appears on the screen...
X	    'appending' is only true if the file already exists 
X	**/
X
X	register int save_current;
X	
X	dprint(4, (debugfile, "\tSaving message %d to file...\n", number));
X
X	save_current = current;
X	current = number+1;
X	copy_message("", fd, FALSE, FALSE);
X	current = save_current;
X
X	if (resolve_mode)
X	  setit(header_table[number].status, DELETED); /* deleted, but ...   */
X	clearit(header_table[number].status, TAGGED);  /* not tagged anymore */
X	clearit(header_table[number].status, NEW);     /* it's not new now!  */
X
X	if (appending)
X	  error2("Message %d appended to file %s", number+1, filename);
X	else
X	  error2("Message %d saved to file %s", number+1, filename);
X
X	if (! silently)
X	  show_new_status(number);	/* update screen, if needed */
X
X	if (pause && ! silently) sleep(2);
X}
X
Xint
Xexpand_filename(filename)
Xchar *filename;
X{
X	/** Expands '~' and '=' to specified file names, also will try to 
X	    expand shell variables if encountered.. '+' and '%' are synonymous 
X	    with '=' (folder dir)... **/
X
X	char buffer[SLEN], varname[SLEN], env_value[SLEN], *ptr;
X	register int i = 1, index = 0;
X	char *getenv();
X
X	ptr = filename;
X	while (*ptr == ' ') ptr++;	/* leading spaces GONE! */
X	strcpy(filename, ptr);
X
X	/** New stuff - make sure no illegal char as last **/
X
X	if (lastch(filename) == '\n' || lastch(filename) == '\r')
X	  lastch(filename) = '\0';
X	  
X	if (filename[0] == '~') {
X	  sprintf(buffer, "%s%s%s", home, 
X		(filename[1] != '/' && lastch(folders) != '/')? "/" : "",
X	  	(char *) filename + 1);
X	  strcpy(filename, buffer);
X	}
X	else if (filename[0] == '=' || filename[0] == '+' || 
X	 	 filename[0] == '%') {
X	  if (strlen(folders) == 0) {
X	    dprint(3, (debugfile, 
X		   "Error: maildir not defined - can't expand '%c' (%s)\n",
X		   filename[0], "expand_filename"));
X	    error1("MAILDIR not defined.  Can't expand '%c'", filename[0]);
X	    return(0);
X	  }
X	  sprintf(buffer, "%s%s%s", folders, 
X		(filename[1] != '/' && lastch(folders) != '/')? "/" : "",
X		(char *) filename + 1);
X	  strcpy(filename, buffer);
X	}
X	else if (filename[0] == '$') {	/* env variable! */
X	  while (isalnum(filename[i]))
X	    varname[index++] = filename[i++];
X	  varname[index] = '\0';
X
X	  env_value[0] = '\0';			/* null string for strlen! */
X	  if (getenv(varname) != NULL)
X	    strcpy(env_value, getenv(varname));
X
X	  if (strlen(env_value) == 0) {
X	    dprint(3,(debugfile, 
X		    "Error: Can't expand environment variable $%s (%s)\n",
X		    varname, "expand_filename"));
X	    error1("Don't know what the value of $%s is!", varname);
X	    return(0);
X	  }
X
X	  sprintf(buffer, "%s%s%s", env_value, 
X		  (filename[i] != '/' && lastch(env_value) != '/')? "/" : "",
X		  (char *) filename + i);
X	  strcpy(filename, buffer);
X	}
X	  
X	return(1);
X}
SHAR_EOF
chmod 0444 src/file.c || echo "restore of src/file.c fails"
echo "x - extracting src/file_util.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/file_util.c &&
X
Xstatic char rcsid[] = "@(#)$Id: file_util.c,v 2.1 88/09/15 20:28:21 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	file_util.c,v $
X * Revision 2.1  88/09/15  20:28:21  syd
X * checked in with -k by syd at 88.09.15.20.28.21.
X * 
X * 88/09/01 Rob Bernardo <gatech!pbhyf.PacBell.COM!rob>
X *	1. It allows the user to use the -z flags and -f [filename] flag
X *	2. Puts elm in raw mode earlier on than before so that any commands
X *
X * 88/08/27 ssw
X *	add deluth patches
X *
X * Revision 2.1  88/07/21  09:58:28  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:04  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** File oriented utility routines for ELM 
X
X**/
X
X#include "headers.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <ctype.h>
X#include <errno.h>
X
X#ifdef BSD
X# undef tolower
X#endif
X
X#include <signal.h>
X#include <errno.h>
X
X#ifdef BSD
X# include <sys/wait.h>
X#endif
X
Xextern int errno;		/* system error number */
X
Xchar *error_name(), *error_description(), *strcpy(), *getlogin();
Xlong  fsize();
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....  (also
X	    see "fsize()" to see how we can get the same information
X	    on an opened file descriptor slightly more quickly)
X	**/
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	    dprint(1,(debugfile,
X		     "Error: errno %s on fstat of file %s (bytes)\n", 
X		     error_name(errno), name));
X	    Write_to_screen("\n\rError attempting fstat on file %s!\n\r",
X		     1, name);
X	    Write_to_screen("** %s - %s **\n\r", 2, error_name(errno),
X		  error_description(errno));
X	    emergency_exit();
X	  }
X	  else
X	    ok = 0;
X	
X	return(ok ? (long) buffer.st_size : 0L);
X}
X
Xint
Xcan_access(file, mode)
Xchar *file; 
Xint   mode;
X{
X	/** returns ZERO iff user can access file or "errno" otherwise **/
X
X	int the_stat = 0, pid, w; 
X	void _exit(), exit();
X#ifdef BSD
X	union wait status;
X#else
X	int status;
X#endif
X	register int (*istat)(), (*qstat)();
X	
X#ifdef NO_VM		/* machine without virtual memory!! */
X	if ((pid = fork()) == 0) {
X#else
X	if ((pid = vfork()) == 0) {
X#endif
X	  setgid(groupid);
X	  setuid(userid);		/** back to normal userid **/
X
X	  errno = 0;
X
X	  if (access(file, mode) == 0) 
X	    _exit(0);
X	  else 
X	    _exit(errno != 0? errno : 1);	/* never return zero! */
X	  _exit(127);
X	}
X
X	istat = signal(SIGINT, SIG_IGN);
X	qstat = signal(SIGQUIT, SIG_IGN);
X
X	while ((w = wait(&status)) != pid && w != -1)
X		;
X
X#ifdef BSD
X	the_stat = status.w_retcode;
X#else
X	the_stat = status;
X#endif
X
X	signal(SIGINT, istat);
X	signal(SIGQUIT, qstat);
X
X	return(the_stat);
X}
X
Xint
Xcan_open(file, mode)
Xchar *file, *mode;
X{
X	/** Returns 0 iff user can open the file.  This is not
X	    the same as can_access - it's used for when the file might
X	    not exist... **/
X
X	FILE *fd;
X	int the_stat = 0, pid, w; 
X	void _exit(), exit();
X#ifdef BSD
X	union wait status;
X#else
X	int status;
X#endif
X	register int (*istat)(), (*qstat)();
X	
X#ifdef NO_VM		/* machine without virtual memory!! */
X	if ((pid = fork()) == 0) {
X#else
X	if ((pid = vfork()) == 0) {
X#endif
X	  setgid(groupid);
X	  setuid(userid);		/** back to normal userid **/
X	  errno = 0;
X	  if ((fd = fopen(file, mode)) == NULL)
X	    _exit(errno);
X	  else {
X	    fclose(fd);		/* don't just LEAVE it! */
X	    _exit(0);
X	  }
X	  _exit(127);
X	}
X
X	istat = signal(SIGINT, SIG_IGN);
X	qstat = signal(SIGQUIT, SIG_IGN);
X
X	while ((w = wait(&status)) != pid && w != -1)
X		;
X
X#ifdef BSD
X	the_stat = status.w_retcode;
X#else
X	the_stat = status;
X#endif
X	
X	signal(SIGINT, istat);
X	signal(SIGQUIT, qstat);
X
X	return(the_stat);
X}
X
Xint
Xcopy(from, to)
Xchar *from, *to;
X{
X	/** this routine copies a specified file to the destination
X	    specified.  Non-zero return code indicates that something
X	    dreadful happened! **/
X
X	FILE *from_file, *to_file;
X	char buffer[VERY_LONG_STRING];
X	
X	if ((from_file = fopen(from, "r")) == NULL) {
X	  dprint(1, (debugfile, "Error: could not open %s for reading (copy)\n",
X		 from));
X	  error1("could not open file %s", from);
X	  return(1);
X	}
X
X	if ((to_file = fopen(to, "w")) == NULL) {
X	  dprint(1, (debugfile, "Error: could not open %s for writing (copy)\n",
X		 to));
X	  error1("could not open file %s", to);
X	  return(1);
X	}
X
X	while (fgets(buffer, VERY_LONG_STRING, from_file) != NULL)
X	  fputs(buffer, to_file);
X
X	fclose(from_file);
X	fclose(to_file);
X	chown( to, userid, groupid);
X
X	return(0);
X}
X
Xmove(from, to)
Xchar *from, *to;
X{
X	/** This routine moves a specified file to the destination
X	    specified.  It starts by trying to it all by link'ing.
X	**/
X
X	FILE *from_file, *to_file;
X	char buffer[VERY_LONG_STRING];
X	
X	if (access(from, ACCESS_EXISTS) == -1) /* doesn't exist? */
X	  return;
X
X	/** does the dest file exist?? **/
X
X	if (access(to, ACCESS_EXISTS) != -1) { /* dest DOES exist! */
X	  printf("File %s already exists!  Overwriting...\n", to);
X	  (void) unlink(to);
X	}
X
X	/** first off, let's try to link() it **/
X
X	if (link(from, to) != -1) {	/* yeah! */
X#ifdef DONT_JUST_LINK_EM_TOGETHER
X	  (void) unlink(from);
X#endif
X	  return;
X	}
X
X	/** nope.  Let's open 'em both up and move the data... **/
X
X	if ((from_file = fopen(from, "r")) == NULL) {
X	  dprint(1, (debugfile, "Error: could not open %s for reading (copy)\n",
X		 from));
X	  printf("Could not open file %s for reading\n", from);
X	  return;
X	}
X
X	if ((to_file = fopen(to, "w")) == NULL) {
X	  dprint(1, (debugfile, "Error: could not open %s for writing (copy)\n",
X		 to));
X	  printf("Could not open file %s for writing\n", to);
X	  return;
X	}
X
X	while (fgets(buffer, VERY_LONG_STRING, from_file) != NULL)
X	  fputs(buffer, to_file);
X
X	fclose(from_file);
X	fclose(to_file);
X	chown( to, userid, groupid);
X
X#ifdef DONT_JUST_LINK_EM_TOGETHER
X	(void) unlink(from);
X#endif
X
X	return;
X}
X
X
Xint
Xappend(fd, filename)
XFILE *fd;
Xchar *filename;
X{
X	/** This routine appends the specified file to the already
X	    open file descriptor.. Returns non-zero if fails.  **/
X
X	FILE *my_fd;
X	char buffer[VERY_LONG_STRING];
X	
X	if ((my_fd = fopen(filename, "r")) == NULL) {
X	  dprint(1, (debugfile,
X		"Error: could not open %s for reading (append)\n", filename));
X	  return(1);
X	}
X
X	while (fgets(buffer, VERY_LONG_STRING, my_fd) != NULL)
X	  fputs(buffer, fd);
X
X	fclose(my_fd);
X
X	return(0);
X}
X
Xint
Xcheck_mailfile_size()
X{
X	/** Check to ensure we have mail.  Only used with the '-z'
X	    starting option. **/
X
X	char filename[SLEN], *getlogin();
X	struct stat buffer;
X
X	if (infile[0] != '\0')
X	  strcpy(filename, infile);
X	else {
X	  strcpy(username,getlogin());
X	  if (strlen(username) == 0)
X	    cuserid(username);
X
X	  sprintf(filename,"%s%s", mailhome, username);
X	}
X
X	if (stat(filename, &buffer) == -1) {
X	  printf(" You have no mail.\n");
X	  return(-1);
X	}
X	else if (buffer.st_size < 2) { 		/* maybe one byte??? */
X	  printf("You have no mail to read.\n");
X	  return(-1);
X	}
X	return(0);
X}
X
Xcreate_readmsg_file()
X{
X	/** Creates the file ".current" in the users home directory
X	    for use with the "readmsg" program.
X	**/
X
X	FILE *fd;
X	char buffer[SLEN];
X
X	sprintf(buffer,"%s/%s", home, readmsg_file);
X
X	if ((fd = fopen (buffer, "w")) == NULL) {
X	  dprint(1, (debugfile, 
X		 "Error: couldn't create file %s - error %s (%s)\n",
X		 buffer, error_name(errno), "create_readmsg_file"));
X	  return;	/* no error to user */
X	}
X
X	if (current)
X	  fprintf(fd, "%d\n", header_table[current-1].index_number);
X	else
X	  fprintf(fd, "\n");
X
X	fclose(fd);
X	chown( buffer, userid, groupid);
X}
X
Xlong fsize(fd)
XFILE *fd;
X{
X	/** return the size of the current file pointed to by the given
X	    file descriptor - see "bytes()" for the same function with
X	    filenames instead of open files...
X	**/
X
X	struct stat buffer;
X
X	(void) fstat(fd, &buffer);
X
X	return( (long) buffer.st_size );
X}
SHAR_EOF
chmod 0444 src/file_util.c || echo "restore of src/file_util.c fails"
echo "x - extracting src/fileio.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/fileio.c &&
X
Xstatic char rcsid[] = "@(#)$Id: fileio.c,v 2.1 88/09/15 20:28:24 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	fileio.c,v $
X * Revision 2.1  88/09/15  20:28:24  syd
X * checked in with -k by syd at 88.09.15.20.28.24.
X * 
X * Revision 2.1  88/07/21  09:58:30  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:05  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** File I/O routines, including deletion from the mailbox! 
X
X**/
X
X#include "headers.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <ctype.h>
X#include <errno.h>
X
X#ifdef BSD
X#undef tolower
X#endif
X
Xextern int errno;
X
Xchar *error_name();
X
Xcopy_message(prefix, dest_file, remove_header, remote)
Xchar *prefix;
XFILE *dest_file;
Xint  remove_header, remote;
X{
X	/** Copy current message to destination file, with optional 'prefix' 
X	    as the prefix for each line.  If remove_header is true, it will 
X	    skip lines in the message until it finds the end of header line...
X            then it will start copying into the file... If remote is true
X	    then it will append "remote from <hostname>" at the end of the
X	    very first line of the file (for remailing) 
X
X	    If "forwarding" is true then it'll do some nice things to
X	    ensure that the forwarded message looks pleasant (e.g. remove
X	    stuff like ">From " lines and "Received:" lines, and prefix
X	    the entire message with "Forwarded message:\n" etc etc)
X	**/
X
X    char buffer[LONG_SLEN];
X    register int ok = 1, lines, in_header = 1, first_line = TRUE,
X	     ignoring = FALSE;
X
X    /** get to the first line of the message desired **/
X
X    if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
X       dprint(1, (debugfile, 
X		"ERROR: Attempt to seek %d bytes into file failed (%s)",
X		header_table[current-1].offset, "copy_message"));
X       error1("ELM [seek] failed trying to read %d bytes into file",
X	     header_table[current-1].offset);
X       return;
X    }
X
X    /* how many lines in message? */
X
X    lines = header_table[current-1].lines;
X
X    /* set up for forwarding just in case... */
X
X    if (forwarding) remove_header = FALSE;
X
X    /* now while not EOF & still in message... copy it! */
X
X    while (ok && lines--) {
X      ok = (int) (fgets(buffer, LONG_SLEN, mailfile) != NULL);
X      if (strlen(buffer) < 2) in_header = 0;
X      if (ok) 
X	if (! (remove_header && in_header))
X	  if (first_line && remote) {
X	    no_ret(buffer);
X	    fprintf(dest_file, "%s%s remote from %s\n",
X		    prefix, buffer, hostname);
X	    first_line = FALSE;
X	  }
X	  else if (! in_header && first_word(buffer, "From ")) {
X	    dprint(1, (debugfile,
X		   "\n*** Internal Problem...Tried to add the following;\n"));
X	    dprint(1, (debugfile,
X		   "  '%s'\nto output file (copy_message) ***\n", buffer));
X	    ok = 0;	/* STOP NOW! */
X	  }
X	  else if (! in_header || ! forwarding)
X	    fprintf(dest_file, "%s%s", prefix, buffer);
X	  else if (forwarding) {
X	    if (first_line) {
X	      first_line = FALSE;
X		fprintf(dest_file, "Forwarded message:\n\n");
X	    }
X	    else if (first_word(buffer, "Received:"   ) ||
X		     first_word(buffer, ">From"       ) ||
X		     first_word(buffer, "Return-Path:"))
X	      ignoring = TRUE;
X	    else if (ignoring && ! whitespace(buffer[0])) {
X	      ignoring = FALSE;
X	      fprintf(dest_file, "%s%s", prefix, buffer);
X	    }
X	    else if (! ignoring || ! in_header)
X	      fprintf(dest_file, "%s%s", prefix, buffer);
X	  }
X    }
X    if (strlen(buffer) + strlen(prefix) > 1)
X      fprintf(dest_file, "\n");	/* blank line to keep mailx happy *sigh* */
X}
X
X/********  the following routines are for a nice clean way to preserve
X   	   the stats related to the file we're currently reading and all 
X*********/
X
Xstatic struct stat statbuff;
X
Xsave_file_stats(fname)
Xchar *fname;
X{
X	struct stat *stat();
X
X	stat(fname, &statbuff);
X
X	dprint(2, (debugfile, "** saved stats for file %s **\n", fname));
X}
X
Xrestore_file_stats(fname)
Xchar *fname;
X{
X	/** restore the file mode, but set our umask to 0 first to
X	    ensure that doesn't get in the way... **/
X
X	int old_umask, i;
X
X	old_umask = umask(0);
X
X	i = chmod(fname, statbuff.st_mode & 0777);
X
X	dprint(2, (debugfile, "** chmod(%s, %.3o) returns %d [errno=%d] **\n",
X		   fname, statbuff.st_mode & 0777, i, errno));
X
X	i = chown(fname, statbuff.st_uid, statbuff.st_gid);
X
X	dprint(2, (debugfile, "** chown(%s, %d, %d) returns %d [errno=%d] **\n",
X		   fname, statbuff.st_uid, statbuff.st_gid, i, errno));
X
X	(void) umask(old_umask);
X}
X
X/** and finally, here's something for that evil trick: site hiding **/
X
X#ifdef SITE_HIDING
X
Xint
Xis_a_hidden_user(specific_username)
Xchar *specific_username;
X{
X	/** Returns true iff the username is present in the list of
X	   'hidden users' on the system.
X	**/
X	
X	FILE *hidden_users;
X	char  buffer[SLEN];
X
X	this line is deliberately inserted to ensure that you THINK
X	about what you're doing, and perhaps even contact the author
X	of Elm before you USE this option...
X
X	if ((hidden_users = fopen (HIDDEN_SITE_USERS,"r")) == NULL) {
X	  dprint(1, (debugfile,
X		  "Couldn't open hidden site file %s [%s]\n",
X		  HIDDEN_SITE_USERS, error_name(errno)));
X	  return(FALSE);
X	}
X
X	while (fscanf(hidden_users, "%s", buffer) != EOF)
X	  if (strcmp(buffer, specific_username) == 0) {
X	    dprint(3, (debugfile, "** Found user '%s' in hidden site file!\n",
X		    specific_username));
X	    fclose(hidden_users);
X	    return(TRUE);
X	  }
X
X	fclose(hidden_users);
X	dprint(3, (debugfile, 
X		"** Couldn't find user '%s' in hidden site file!\n",
X		specific_username));
X
X	return(FALSE);
X}
X
X#endif
SHAR_EOF
chmod 0444 src/fileio.c || echo "restore of src/fileio.c fails"
echo "x - extracting src/forms.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/forms.c &&
X
Xstatic char rcsid[] = "@(#)$Id: forms.c,v 2.1 88/09/15 20:28:28 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X *******************************************************************************
X * Bug reports, patches, comments, suggetions should be sent to:
X *
X *	Syd Weinstein, Elm Corrdinator
X *	syd@dsinc.UUCP			dsinc!syd
X *
X *******************************************************************************
X * $Log:	forms.c,v $
X * Revision 2.1  88/09/15  20:28:28  syd
X * checked in with -k by syd at 88.09.15.20.28.28.
X * 
X * Revision 2.1  88/07/21  09:58:32  edc
X * Final hacks and cleanup to the 2.1 alpha test release.
X * 
X * Revision 2.0  88/06/27  17:25:05  edc
X * The original 2.0 gamma sources as leaked from HP
X * 
X *
X *
X ******************************************************************************/
X
X/** This set of files supports the 'forms' options (AT&T Mail Forms) to
X    the mail system.  The specs are drawn from a document from AT&T entitled
X    "Standard for Exchanging Forms on AT&T Mail", version 1.9.
X
X**/
X
X/** Some notes on the format of a FORM;
X
X	First off, in AT&T Mail parlance, this program only supports SIMPLE
X	forms, currently.  This means that while each form must have three
X 	sections;
X
X		[options-section]
X		***
X		[form-image]
X		***
X		[rules-section]
X
X	this program will ignore the first and third sections completely.  The
X	program will assume that the user merely enteres the form-image section,
X	and will append and prepend the triple asterisk sequences that *MUST*
X	be part of the message.  The messages are also expected to have a 
X	specific header - "Content-Type: mailform" - which will be added on all
X	outbound mail and checked on inbound...
X**/
X
X#include "headers.h"
X#include <errno.h>
X
Xextern int errno;
X
Xchar *error_name(), *strcat(), *strcpy();
X
Xcheck_form_file(filename)
Xchar *filename;
X{
X	/** This routine returns the number of fields in the specified file,
X	    or -1 if an error is encountered. **/
X
X	FILE *form;
X	char buffer[SLEN];
X	register int field_count = 0;
X
X	if ((form = fopen(filename, "r")) == NULL) {
X	  error2("Error %s trying to open %s to check fields!",
X		  error_name(errno), filename);
X	  return(-1);
X	}
X	
X	while (fgets(buffer, SLEN, form) != NULL) {
X	  field_count += occurances_of(COLON, buffer);
X	}
X
X	fclose(form);
X
X	return(field_count);
X}
X
Xformat_form(filename)
Xchar *filename;
X{
X	/** This routine accepts a validated file that is the middle 
X	    section of a form message and prepends and appends the appropriate 
X	    instructions.  It's pretty simple. 
X	    This returns the number of forms in the file, or -1 on errors
X	**/
X	
X	FILE *form, *newform;
X	char  newfname[SLEN], buffer[SLEN];
X	register form_count = 0;
X
X	dprint(4, (debugfile, "Formatting form file '%s'\n", filename));
X
X	/** first off, let's open the files... **/
X
X	if ((form = fopen(filename, "r")) == NULL) {
X	  error("Can't read the message to validate the form!");
X	  dprint(1, (debugfile,
X              "** Error encountered opening file \"%s\" - %s (check_form) **\n",
X	      filename, error_name(errno)));
X	  return(-1);
X	}
X
X	sprintf(newfname, "%s%d", temp_form_file, getpid());
X
X	if ((newform = fopen(newfname, "w")) == NULL) {
X	  error("Couldn't open newform file for form output!");
X	  dprint(1, (debugfile, 
X              "** Error encountered opening file \"%s\" - %s (check_form) **\n",
X	      newfname, error_name(errno)));
X	  return(-1);
X	}
X
X	/** the required header... **/
X
X	/* these are actually the defaults, but let's be sure, okay? */
X
X	fprintf(newform, "WIDTH=78\nTYPE=SIMPLE\nOUTPUT=TEXT\n***\n");
X
X	/** and let's have some fun transfering the stuff across... **/
X
X	while (fgets(buffer, SLEN, form) != NULL) {
X	  fputs(buffer, newform);
X	  form_count += occurances_of(COLON, buffer);
X	}
X
X	fprintf(newform, "***\n");	/* that closing bit! */
X
X	fclose(form);
X	fclose(newform);
X
X	if (form_count > 0) {
X	  if (unlink(filename) != 0) {
X	    error2("Error %s unlinking file %s", error_name(errno), filename);
X	    return(-1);
X	  }
X	  if (link(newfname, filename)) {
X	    error3("Error %s linking %s to %s", error_name(errno), 
X		    newfname, filename);
X	    return(-1);
X	  }
X	}
X
X	if (unlink(newfname)) {
X	  error2("Error %s unlinking file %s", error_name(errno), newfname);
X	  return(-1);	
X	}
X
X	return(form_count);
X}
X
Xint
Xmail_filled_in_form(address, subject)
Xchar *address, *subject;
X{
X	/** This is the interesting routine.  This one will read the
X	    message and prompt the user, line by line, for each of
X	    the fields...returns non-zero if it succeeds
X	**/
X
X	FILE  	     *fd;
X	register int lines = 0, count;
X	char         buffer[SLEN], *ptr;
X
X	dprint(4, (debugfile, 
X		"replying to form with;\n\taddress=%s and\n\t subject=%s\n",
X		 address, subject));
X
X        if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
X	  dprint(1, (debugfile,
X		   "Error: seek %ld resulted in errno %s (%s)\n", 
X		   header_table[current-1].offset, error_name(errno), 
X		   "mail_filled_in_form"));
X	  error2("ELM [seek] couldn't read %d bytes into file (%s)",
X	         header_table[current-1].offset, error_name(errno));
X	  return(0);
X        }
X 
X	/* now we can fly along and get to the message body... */
X
X	while ((ptr = fgets(buffer, SLEN, mailfile)) != NULL) {
X	  if (strlen(buffer) == 1)	/* <return> only */
X	    break;
X	  else if (strncmp(buffer,"From ", 5) == 0 && lines++ > 0) {
X	    error("No form in this message!?");
X	    return(0);
X	  }
X	}
X
X	if (ptr == NULL) {
X	  error("No form in this message!?");
X	  return(0);
X	}
X
X	dprint(6, (debugfile, "- past header of form message -\n"));
X	
X	/* at this point we're at the beginning of the body of the message */
X
X	/* now we can skip to the FORM-IMAGE section by reading through a 
X	   line with a triple asterisk... */
X
X	while ((ptr = fgets(buffer, SLEN, mailfile)) != NULL) {
X	  if (strcmp(buffer, "***\n") == 0)
X	    break;	/* we GOT it!  It's a miracle! */	
X	  else if (strncmp(buffer, "From ",5) == 0) {
X	    error("Badly constructed form.  Can't reply!");
X	    return(0);
X	  }
X	}
X
X	if (ptr == NULL) {
X	  error("Badly constructed form.  Can't reply!");
X	  return(0);
X	}
X
X	dprint(6, (debugfile, "- skipped the non-forms-image stuff -\n"));
X	
X	/* one last thing - let's open the tempfile for output... */
X	
X	sprintf(buffer, "%s%d", temp_form_file, getpid());
X
X	dprint(2, (debugfile,"-- forms sending using file %s --\n", buffer));
X
X	if ((fd = fopen(buffer,"w")) == NULL) {
X	  error2("Can't open \"%s\" as output file! (%s)", buffer,
X		 error_name(errno));
X	  dprint(1, (debugfile,
X		  "** Error %s encountered trying to open temp file %s;\n",
X		  error_name(errno), buffer));
X	  return(0);
X	}
X
X	/* NOW we're ready to read the form image in and start prompting... */
X
X	Raw(OFF);
X	ClearScreen();
X
X	while ((ptr = fgets(buffer, SLEN, mailfile)) != NULL) {
X	  dprint(9, (debugfile, "- read %s", buffer));
X	  if (strcmp(buffer, "***\n") == 0) /* end of form! */
X	    break;
X	 
X	  switch ((count = occurances_of(COLON, buffer))) {
X	    case 0 : printf("%s", buffer);		/* output line */
X		     fprintf(fd, "%s", buffer); 	
X		     break;
X            case 1 : if (buffer[0] == COLON) {
X	             printf(
X"(Enter as many lines as needed, ending with a '.' by itself on a line)\n");
X                     while (gets(buffer) != NULL)
X	               if (strcmp(buffer, ".") == 0)
X	                 break;
X	               else 
X			 fprintf(fd,"%s\n", buffer);
X	             }
X	             else 
X		       prompt_for_entry(buffer, fd);
X	             break;
X            default: prompt_for_multiple_entries(buffer, fd, count);
X	  }
X	}
X
X	Raw(ON);
X	fclose(fd);
X
X	/** let's just mail this off now... **/
X
X	mail_form(address, subject);
X
X	return(1);
X}
X
Xprompt_for_entry(buffer, fd)
Xchar *buffer;
XFILE *fd;
X{
X	/** This is called with an entry of the form "prompt:" and will 
X	    display the prompt and save the prompt and the user reply
X	    in the file "fd"
X	**/
X	
X	char mybuffer[SLEN];
X
X	no_ret(buffer);
X
X	dprint(7, (debugfile, "prompt-for-entry \"%s\"\n", buffer));
X
X	printf("%s ", buffer);	fflush(stdout);
X
X	gets(mybuffer);
X
X	fprintf(fd, "%s: %s\n", buffer, mybuffer);
X}
X
Xprompt_for_multiple_entries(buffer, fd, entries)
Xchar *buffer;
XFILE *fd;
Xint  entries;
X{
X	/** Almost the same as the above routine, this one deals with lines
X	    that have multiple colons on them.  It must first figure out how
X	    many spaces to allocate for each field then prompts the user, 
X	    line by line, for the entries...
X	**/
X
X	char mybuffer[SLEN], prompt[SLEN], spaces[SLEN];
X	register int  field_size, i, j, offset = 0, extra_tabs = 0;
X
X	dprint(7, (debugfile, 
X		"prompt-for-multiple [%d] -entries \"%s\"\n", entries,
X		buffer));
X
X	strcpy(prompt, "No Prompt Available:");
X
X	while (entries--) {
X	  j=0; 
X	  i = chloc((char *) buffer + offset, COLON) + 1;
X	  while (j < i - 1) {
X	    prompt[j] = buffer[j+offset];
X	    j++;
X	  }
X	  prompt[j] = '\0';
X
X	  field_size = 0;
X
X	  while (whitespace(buffer[i+offset])) {
X	    if (buffer[i+offset] == TAB) {
X	      field_size += 8 - (i % 8);
X	      extra_tabs += (8 - (i % 8)) - 1;
X	    }
X	    else
X	      field_size += 1;
X	    i++;
X	  }
X
X	  offset += i;
X	
X	  if (field_size == 0) 	/* probably last prompt in line... */
X	    field_size = 80 - (offset + extra_tabs);
X
X	  prompt_for_sized_entry(prompt, mybuffer, field_size);
X
X	  spaces[0] = ' ';	/* always at least ONE trailing space... */
X	  spaces[1] = '\0';
X
X	  for (j = strlen(mybuffer); j < field_size; j++)
X	    strcat(spaces, " ");
X
X	  fprintf(fd, "%s: %s%s", prompt, mybuffer, spaces);
X	  fflush(fd);
X	}
X
X	fprintf(fd, "\n");
X}
X
Xprompt_for_sized_entry(prompt, buffer, field_size)
Xchar *prompt, *buffer;
Xint   field_size;
X{
X	/* This routine prompts for an entry of the size specified. */
X
X	register int i;
SHAR_EOF
echo "End of part 12"
echo "File src/forms.c is continued in part 13"
echo "13" > s2_seq_.tmp
exit 0
-- 
=====================================================================
Sydney S. Weinstein, CDP, CCP                   Elm Coordinator
Datacomp Systems, Inc.				Voice: (215) 947-9900
{allegra,bellcore,bpa,vu-vlsi}!dsinc!syd	FAX:   (215) 938-0235