[mod.sources] v06i030: Elm mail system

sources-request@mirror.UUCP (06/30/86)

Submitted by: Dave Taylor <pyramid!hplabs!hpldat!taylor>
Mod.sources: Volume 6, Issue 30
Archive-name: elm/Part05

# Continuation of Shell Archive, created by hpldat!taylor

# This is part 5

# To unpack the enclosed files, please use this file as input to the
# Bourne (sh) shell.  This can be most easily done by the command;
#     sh < thisfilename


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

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

filename="src/Makefile"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/Makefile...
fi

cat << 'END-OF-FILE' > $filename
#
#  Makefile for the ELM mail program.
#
#         (C) Copyright 1986, Dave Taylor
#
#  Last modification: March 7th, 1986

SHELL=/bin/sh

##############################
#
# if on a BSD system;
#   DEFINE=-DBSD
#   LIB2  = -lcurses
# else if on a UTS system;
#   DEFINE=-DUTS
#   LIB2  = -la -lq
# else if on a SUN system;
#   DEFINE=-DBSD -DSUN
#   LIB2  = -lcurses
# else if on a Pyramid system;
#   DEFINE=-DBSD -DNO_VAR_ARGS
#   LIB2  = -lcurses
# else

    DEFINE=
    LIB2  =

# IF you're on an ACSnet system (Australia) then
# you'll want to uncomment the following;

#   DEFINE= ${DEFINE} -DACSNET

##############################

CFILES=	addr_utils.c alias.c aliasdb.c aliaslib.c args.c bounceback.c       \
	calendar.c connect_to.c curses.c date.c delete.c domains.c edit.c   \
	encode.c errno.c file.c file_utils.c fileio.c hdrconfg.c help.c     \
	initialize.c input_utils.c leavembox.c mailmsg1.c mailmsg2.c        \
	mailtime.c mkhdrs.c elm.c newmbox.c notesfile.c options.c opt_utils.c \
	output_utils.c pattern.c quit.c read_rc.c remail.c reply.c 	    \
	return_addr.c savecopy.c screen.c showmsg.c signals.c softkeys.c    \
	sort.c strings.c syscall.c utils.c validname.c getopt.c string2.c

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

OBJS=	addr_utils.o alias.o aliasdb.o aliaslib.o args.o bounceback.o       \
	calendar.o connect_to.o curses.o date.o delete.o domains.o edit.o   \
	encode.o errno.o file.o file_utils.o fileio.o hdrconfg.o help.o     \
	initialize.o input_utils.o leavembox.o mailmsg1.o mailmsg2.o        \
	mailtime.o mkhdrs.o elm.o newmbox.o notesfile.o options.o opt_utils.o \
	output_utils.o pattern.o quit.o read_rc.o remail.o reply.o 	    \
	return_addr.o savecopy.o screen.o showmsg.o signals.o softkeys.o    \
	sort.o strings.o syscall.o utils.o validname.o getopt.o string2.o

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

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

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

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

# curses.c : curses.q
# 	@../bin/quickscreen curses.q
#
# curses.q :
# 	@cp curses.c curses.q

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

lint: LINT.OUT

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

listing: LISTING

LISTING: Makefile INDEX ${HEADERS} ${CFILES}
	@echo adding file 'Makefile'...
	@/bin/echo \\f > LISTING
	@cat Makefile >> LISTING
	@echo adding file 'INDEX'...
	@/bin/echo \\f >> LISTING
	@cat INDEX >> LISTING
	@../bin/makelisting ${HEADERS} ${CFILES}
	@echo LISTING generated.

index: INDEX

INDEX: ${CFILES} ${HEADERS}
	@echo Creating function definition index
	@index *.c | sort > INDEX
	@echo File INDEX generated
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 2822 ]
  then
    echo $filename changed - should be 2822 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

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

filename="src/alias.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/alias.c...
fi

cat << 'END-OF-FILE' > $filename
/**			alias.c			**/

/** This file contains alias stuff

    (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"
#include <errno.h>

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

extern int errno;

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

	char fname[SLEN];
	int  hash;

	if ((hash = open(system_hash_file, O_RDONLY)) == -1) {
	 dprint1(1,
		"Warning: Can't read system hash file %s (read_alias_files)\n",
		system_hash_file);
	  goto user;
	}

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

	/* and data file opened.. */

	if ((system_data = open(system_data_file, O_RDONLY)) == -1) {
	 dprint1(1, 
	         "Warning: Can't read system data file %s (read_alias_files)\n",
		 system_data_file);
	  goto user;
	}

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

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

	if ((hash = open(fname, O_RDONLY)) == -1) {
	 dprint1(1,"Warning: Can't read user hash file %s (read_alias_files)\n",
		  fname);
	  return;
	}

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

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

	if ((user_data = open(fname, O_RDONLY)) == -1) {
	 dprint1(1,
	         "Warning: can't read user data file %s  (read_alias_files)\n",
		 fname);
	  return;
	}

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

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

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

	PutLine0(LINES-2,0,"Enter alias name: ");
	CleartoEOLN();
	Raw(OFF);
	gets(name, 20);
	Raw(ON);
	if (strlen(name) == 0) 
	  return(0);
	if ((address = get_alias_address(name, 0, 0)) != NULL) {
	  dprint1(2, "Attempt to add a duplicate alias [%s] (add_alias)\n",
		   address); 
	  if (address[0] == '!') {
	    address[0] = ' ';
	    error1("already a group with that name:%s", address);
	  }
	  else
	    error1("already an alias for that: %s", address);
	  return(0);
	}
	PutLine1(LINES-2,0,"Full name for %s: ", name);
	CleartoEOLN();
	Raw(OFF);
	gets(comment,SLEN);
	Raw(ON);
	if (strlen(comment) == 0) strcpy(comment, name);  
	PutLine1(LINES-2,0,"Enter address for %s: ",name);
	CleartoEOLN();
	Raw(OFF);
	gets(address1,LONG_SLEN);
	Raw(ON);
	if (strlen(address1) == 0) {
	  error("No address specified!");
	  return(0);
	}
	add_to_alias_text(name, comment, address1);
	return(1);
}

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

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

	if (current == 0) {
	 dprint0(3,"Add current alias called without any current message!\n");
	 error("No message to alias to!");
	 return(0);
	}
	
	PutLine0(LINES-2,0,"Current message address aliased to: ");
	CleartoEOLN();
	Raw(OFF);
	gets(name, 20);
	Raw(ON);
	if (strlen(name) == 0)	/* cancelled... */
	  return(0);
	if ((address = get_alias_address(name, 0, 0)) != NULL) {
	 dprint1(3,
	         "Attempt to add a duplicate alias [%s] (add_current_alias)\n",
		 address); 
	  if (address[1] == '!') {
	    address[0] = ' ';
	    error1("already a group with that name:%s", address);
	  }
	  else 
	    error1("already an alias for that: %s", address); 
          return(0);
	}
	PutLine1(LINES-2,0,"Full name of %s: ", name);
	CleartoEOLN();
	Raw(OFF);
	gets(comment, 40);
	Raw(ON);
	get_return(address1);	/* grab the return address of this message */
	optimize_return(address1);
	PutLine3(LINES-2,0,"%s (%s) = %s", comment, name, address1);
	CleartoEOLN();
	add_to_alias_text(name, comment, address1);
	return(1);
}

add_to_alias_text(name, comment, address)
char *name, *comment, *address;
{
	/** Add the data to the user alias text file.  Return zero if we
	    succeeded, 1 if not **/
	
	FILE *file;
	char fname[SLEN];
	
	sprintf(fname,"%s/.alias_text", home);
	
	if ((file = fopen(fname, "a")) == NULL) {
	  dprint2(2, "FILE Failure attempting to add alias to file %s (%s)",
		   fname, "add_to_alias_text");
	  dprint2(2, "** %s - %s **\n", error_name(errno), 
		   error_description(errno));
	  error1("couldn't open %s to add new alias!", fname);
	  return(1);
	}

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

	chown(fname, userid, groupid);

	return(0);
}

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

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

	if (mini_menu)
	  show_alias_menu();

	define_softkeys(ALIAS);

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

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

	    case 's': PutLine0(LINES-2,0,"Check for system: ");
		      CleartoEOS();
		      Raw(OFF);
	              gets(name,NLEN);
		      Raw(ON);
		      if (talk_to(name)) 
#ifdef INTERNET_ADDRESS_FORMAT
			PutLine1(LINES-1,0,
		"You have a direct connection - the address is (user)@%s", 
			name);
#else
			PutLine1(LINES-1,0,
		"You have a direct connection - the address is %s!(user)", 
			name);
#endif
		      else {
		        sprintf(buffer, "(user)@%s", name);
	 	        address = expand_system(buffer, FALSE);
		        if (strlen(address) > strlen(name) + 7)
		          PutLine1(LINES-1,0,"Address is: %.65s", address);
		        else
		          error1("couldn't expand system '%s'", name);
		      }
		      break;

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

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

	error("Adding new aliases...");
	sleep(2);

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

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 7725 ]
  then
    echo $filename changed - should be 7725 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

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

filename="src/curses.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/curses.c...
fi

cat << 'END-OF-FILE' > $filename
/** 			curses.c		**/

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

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

     Modified 5/86 to add memory lock support, thanks to the
     suggested code by Steve Wolf.

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

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

#include "headers.h"

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

#include <ctype.h>

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

#ifdef RAWMODE
# define TTYIN	0
#endif

extern int debug;

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

static int _inraw = 0;                  /* are we IN rawmode?    */
static int _memory_locked = 0;		/* are we IN memlock??   */
static int _line  = -1,			/* initialize to "trash" */
           _col   = -1;

#ifdef UTS
static int _clear_screen = 0;		/* Next i/o clear screen? */
static char _null_string[SLEN];		/* a string of nulls...   */
#endif

#endif

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

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

static
int _lines, _columns;

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

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

int    outchar();			/* char output for tputs */
char  *tgetstr(),     		       /* Get termcap capability */
      *tgoto();				/* and the goto stuff    */

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

	int  tgetent(),      /* get termcap entry */
	error;
	char termname[40];
	
	dprint0(8,"InitScreen()\n");

#ifdef SUN
	if (getenv("TERM") == NULL)
		return(-1);
#endif

#ifdef UTS

	/* use _line for lack of a better variable, what the heck! */

	for (_line = 0; _line < SLEN; _line++)
		_null_string[_line] = '\0';
#endif

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

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

	_line  =  0;		/* where are we right now?? */
	_col   =  0;		/* assume zero, zero...     */

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


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

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

	return(0);
}

char *return_value_of(termcap_label)
char *termcap_label;
{
	/** This will return the string kept by termcap for the 
	    specified capability. Modified to ensure that if 
	    tgetstr returns a pointer to a transient address	
	    that we won't bomb out with a later segmentation
	    fault (thanks to Dave@Infopro for this one!) **/

	static char escape_sequence[20];

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

	dprint1(9,"return_value_of(%s)\n", termcap_label);

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

transmit_functions(newstate)
int newstate;
{
	/** turn function key transmission to ON | OFF **/

	dprint1(9,"transmit_functions(%s)\n", onoff(newstate));

	if (newstate != _intransmit) {
		_intransmit = ! _intransmit;
		if (newstate == ON)
		  tputs(_transmit_on, 1, outchar);
		else 
		  tputs(_transmit_off, 1, outchar);

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

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

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

	dprint2(9,"ScreenSize(_,_) returning %d, %d\n", _lines-1, _columns);

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

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

	dprint0(9,"ClearScreen()\n");

	_line = 0;	/* clear leaves us at top... */
	_col  = 0;

#ifdef UTS
	if (isatube) {
		_clear_screen++;	/* queue up for clearing... */
		return(0);
	}
#endif

	if (!_clearscreen)
		return(-1);

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

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

	char *stuff;

	/* we don't want to change "rows" or we'll mangle scrolling... */

	if (col > COLUMNS) col = COLUMNS;
	if (col < 0)       col = 0;

#ifdef UTS
	if (isatube) {
		at row+1, col+1;
		_line = row;
		_col  = col;
		return(0);
	}
#endif
	if (!_moveto)
		return(-1);

	if (row == _line) {
	  if (col == _col)
	    return(0);
	  else if (abs(col - _col) < 5) {
	    if (col > _col)
	      CursorRight(col - _col);
	    else 
	      CursorLeft(_col - col);
          }
	  else {
	    stuff = tgoto(_moveto, col, row);
	    tputs(stuff, 1, outchar);
	    fflush(stdout);
	  }
	}
	else if (col == _col && abs(row - _line) < 5) {
	  if (row < _line)
	    CursorUp(_line - row);
	  else
	    CursorDown(row - _line);
	}
	else if (_line == row-1 && col == 0) {
	  putchar('\n');	/* that's */
	  putchar('\r');	/*  easy! */
	  fflush(stdout);
	}
	else {
	  stuff = tgoto(_moveto, col, row);
	  tputs(stuff, 1, outchar);
	  fflush(stdout);
	}

	_line = row;	/* to ensure we're really there... */
	_col  = col;

	return(0);
}

CursorUp(n)
int n;
{
	/** move the cursor up 'n' lines **/

	dprint1(9,"CursorUp(%d)\n", n);

	_line = (_line-n > 0? _line - n: 0);	/* up 'n' lines... */

#ifdef UTS
	if (isatube) {
		at _line+1, _col+1;
		return(0);
	}
#endif
	if (!_up)
		return(-1);

	while (n-- > 0)
		tputs(_up, 1, outchar);

	fflush(stdout);
	return(0);
}


CursorDown(n)
int n;
{
	/** move the cursor down 'n' lines **/

	dprint1(9,"CursorDown(%d)\n", n);

	_line = (_line+n < LINES? _line + n: LINES);	/* down 'n' lines... */

#ifdef UTS
	if (isatube) {
		at _line+1, _col+1 ;
		return(0);
	}
#endif

	if (!_down)
		return(-1);

	while (n-- > 0)
		tputs(_down, 1, outchar);

	fflush(stdout);
	return(0);
}


CursorLeft(n)
int n;
{
	/** move the cursor 'n' characters to the left **/

	dprint1(9,"CursorLeft(%d)\n", n);

	_col = (_col - n> 0? _col - n: 0);	/* left 'n' chars... */

#ifdef UTS
	if (isatube) {
		at _line+1, _col+1;
		return(0);
	}
#endif

	if (!_left)
		return(-1);

	while (n-- > 0)
		tputs(_left, 1, outchar);

	fflush(stdout);
	return(0);
}


CursorRight(n)
int n;
{
	/** move the cursor 'n' characters to the right (nondestructive) **/

	dprint1(9,"CursorRight(%d)\n", n);

	_col = (_col+n < COLUMNS? _col + n: COLUMNS);	/* right 'n' chars... */

#ifdef UTS
	if (isatube) {
		at _line+1, _col+1;
		return(0);
	}
#endif

	if (!_right)
		return(-1);

	while (n-- > 0)
		tputs(_right, 1, outchar);

	fflush(stdout);
	return(0);
}


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

	if (!_setbold)
		return(-1);

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


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

	if (!_clearbold)
		return(-1);

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


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

	if (!_setunderline)
		return(-1);

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


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

	if (!_clearunderline)
		return(-1);

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


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

	if (!_sethalfbright)
		return(-1);

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

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

	if (!_clearhalfbright)
		return(-1);

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

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

	if (!_setinverse)
		return(-1);

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


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

	if (!_clearinverse)
		return(-1);

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

int
HasMemlock()
{
	/** returns TRUE iff memory locking is available (a terminal
	    feature that allows a specified portion of the screen to
	    be "locked" & not cleared/scrolled... **/

	return ( _set_memlock && _clear_memlock );
}

static int _old_LINES;

int
StartMemlock()
{
	/** mark the current line as the "last" line of the portion to 
	    be memory locked (always relative to the top line of the
	    screen) Note that this will alter LINES so that it knows
	    the top is locked.  This means that (plus) the program 
	    will scroll nicely but (minus) End memlock MUST be called
	    whenever we leave the locked-memory part of the program! **/

	if (! _set_memlock)
	  return(-1);

	if (! _memory_locked) {

	  _old_LINES = LINES;
	  LINES -= _line;		/* we can't use this for scrolling */

	  tputs(_set_memlock, 1, outchar);
	  fflush(stdout);
	  _memory_locked = TRUE;
	}

	return(0);
}

int
EndMemlock()
{
	/** Clear the locked memory condition...  **/

	if (! _set_memlock)
	  return(-1);

	if (_memory_locked) {
	  LINES = _old_LINES;		/* back to old setting */
  
	  tputs(_clear_memlock, 1, outchar);
	  fflush(stdout);
	  _memory_locked = FALSE;
	}
	return(0);
}


Writechar(ch)
char ch;
{
	/** write a character to the current screen location. **/

#ifdef UTS
	char buffer[2];	/* can't output characters! */
#endif

	dprint1(9,ch >= ' ' ? "Writechar(%c)\n" : "Writechar(^%c)\n",
		  ch >= ' ' ?  ch : ch + 'A' - 1);
#ifdef UTS

	if (isatube) {
	  buffer[0] = ch;
	  buffer[1] = '\0';

	  at _line+1, _col+1;
	  panel (noerase, noinit, noread) {
	    #ON, buffer, 1#
	  }
 	}
	else
#endif
	  putchar(ch);

	if (ch == BACKSPACE)	/* moved BACK one! */
		_col--;
	else if (ch >= ' ')	/* moved FORWARD one! */
		_col++;
}

Write_to_screen(line, argcount, arg1, arg2, arg3)
char *line;
int   argcount, arg1, arg2, arg3;
{
	/** This routine writes to the screen at the current location.
  	    when done, it increments lines & columns accordingly by
	    looking for "\n" sequences... **/

	dprint2(9,"Write_to_screen(%s) [%d args]\n", line, argcount);

	switch (argcount) {
	case 0 :
		PutLine0(_line, _col, line);
		break;
	case 1 :
		PutLine1(_line, _col, line, arg1);
		break;
	case 2 :
		PutLine2(_line, _col, line, arg1, arg2);
		break;
	case 3 :
		PutLine3(_line, _col, line, arg1, arg2, arg3);
		break;
	}
}

PutLine0(x, y, line)
int x,y;
char *line;
{
	/** Write a zero argument line at location x,y **/

	register int i;

	dprint3(8,"PutLine0(%d, %d, %s.30...)\n", x, y, line);

#ifdef UTS
	if (isatube) {
		at x+1, y+1;
		panel (init=_clear_screen, noread, erase=_clear_screen) {
		  #ON, line, strlen(line)-1#
		}
		_clear_screen = 0;
		_col += printable_chars(line);

		/* line wrapped around?? */
		while (_col > COLUMNS) {
		  _col  -= COLUMNS;
		  _line += 1;
		}

		/* now let's figure out if we're supposed to do a "<return>" */

		for (i=0; i < strlen(line); i++)
			if (line[i] == '\n') {
				_line++;
				_col = 0;		/* on new line! */
			}
		return(0);
	}
#endif
	MoveCursor(x,y);
	printf("%s", line);	/* to avoid '%' problems */
	fflush(stdout);
	_col += printable_chars(line);

	while (_col > COLUMNS) { 	/* line wrapped around?? */
		_col -= COLUMNS;
		_line += 1;
	}

	/** now let's figure out if we're supposed to do a "<return>" **/

	for (i=0; i < strlen(line); i++)
		if (line[i] == '\n') {
			_line++;
			_col = 0;		/* on new line! */
		}
}

PutLine1(x,y, line, arg1)
int x,y;
char *line;
char *arg1;
{
	/** write line at location x,y - one argument... **/

	char buffer[SLEN];

	dprint0(9, "PutLine1(...)\n");

	sprintf(buffer, line, arg1);

	PutLine0(x, y, buffer);
}

PutLine2(x,y, line, arg1, arg2)
int x,y;
char *line;
char *arg1, *arg2;
{
	/** write line at location x,y - one argument... **/

	char buffer[SLEN];

	dprint0(9, "PutLine2(...)\n");

	sprintf(buffer, line, arg1, arg2);

	PutLine0(x, y, buffer);
}

PutLine3(x,y, line, arg1, arg2, arg3)
int x,y;
char *line;
char *arg1, *arg2, *arg3;
{
	/** write line at location x,y - one argument... **/

	char buffer[SLEN];

	dprint0(9, "PutLine3(...)\n");

	sprintf(buffer, line, arg1, arg2, arg3);

	PutLine0(x, y, buffer);
}

CleartoEOLN()
{
	/** clear to end of line **/
#ifdef UTS
	char buffer[SLEN];
	register int cols, i = 0;

	if (isatube) {

		dprint0(9, "CleartoEOLN()\n");

		for (cols = _col; cols < COLUMNS; cols++)
			buffer[i++] = ' ';

		buffer[i] = '\0';

		at _line+1, _col+1;
		panel (noerase, noinit, noread) {
		  #ON, buffer, strlen(buffer)-1#
		}

		return(0);
	}
#endif

	dprint0(9, "CleartoEOLN()\n");

	if (!_cleartoeoln)
		return(-1);

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

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

#ifdef UTS
	register int line_at;

	if (isatube) {
	  dprint0(9,"CleartoEOS()\n");
	  for (line_at = _line; line_at < LINES-1; line_at++) {
		panel (noread, noinit, noread) {
	            #@ line_at, 1# #ON, _null_string, COLUMNS# 
		}
	  }
	
  	  return(0);
	}

#endif
	dprint0(9,"CleartoEOS()\n");

	if (!_cleartoeos)
		return(-1);

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

#ifdef RAWMODE

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

	dprint1(8,"Raw(%s)\n", onoff(state));

	if (state == OFF && _inraw) {
#ifdef BSD
		echo();
		nocrmode();
#else
		(void) ioctl(TTYIN, TCSETAW, &_original_tty);
#endif
		_inraw = 0;
	}
	else if (state == ON && ! _inraw) {
#ifdef BSD
		noecho();
		crmode();
#else
		(void) ioctl(TTYIN, TCGETA, &_original_tty);	/** current setting **/

		(void) ioctl(TTYIN, TCGETA, &_raw_tty);    /** again! **/
		_raw_tty.c_iflag &= ~(INLCR | ICRNL |BRKINT);
		_raw_tty.c_iflag |= IXON;
		_raw_tty.c_oflag |= OPOST;
		_raw_tty.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
		_raw_tty.c_lflag &= ~(ICANON | ECHO);
		_raw_tty.c_cc[VMIN] = '\01';
		_raw_tty.c_cc[VTIME] = '\0';
		(void) ioctl(TTYIN, TCSETAW, &_raw_tty);
#endif

		_inraw = 1;
	}
}

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

	register int result;
	char ch;

	dprint0(9,"ReadCh()\n");

	result = read(0, &ch, 1);

	return(result == 0? EOF : ch);
}

#endif

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

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

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 15951 ]
  then
    echo $filename changed - should be 15951 bytes, not $size bytes
  fi

  chmod 666 $filename
fi

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

filename="src/date.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/date.c...
fi

cat << 'END-OF-FILE' > $filename
/**		date.c		**/

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

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

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

#include <ctype.h>

#ifdef BSD
#undef toupper
#endif

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

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

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

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

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

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

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

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

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

	    *************************************
	    ** This routine is not used by ELM **
	    *************************************

	**/

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

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

	return( (char *) buffer);
}

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

	static char buffer[10];
	register int digit;

	digit = day % 10;

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

	return( (char *) buffer);
}

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

	       	      Mon, 12 Aug 85 6:29:08 MST

	**/

	static char buffer[SLEN];	/* static character buffer       */
	struct tm *the_time,		/* Time structure, see CTIME(3C) */
		  *localtime();
	long	   junk;		/* time in seconds....		 */
#ifdef BSD
	struct  timeval  time_val;		
	struct  timezone time_zone;
#endif

#ifdef BSD
	gettimeofday(&time_val, &time_zone);
	junk = time_val.tv_sec;
#else
	junk = (long) time(0);	/* this must be here for it to work! */
#endif
	the_time = localtime(&junk);

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

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

	char   name[4];
	register int i;

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

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

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

	for (i=0; i < 12; i++)
	  if (strncmp(name, arpa_monname[i], 3) == 0)
	    return((char *) monname[i]);
	
	dprint1(2, "Warning: Couldn't expand monthname %s (full_month)\n", 
	        month);

	return( (char *) month);
}

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

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

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

	/* increment the day of the week */

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

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

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

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

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

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

	register int daynum, yearnum;

	daynum = atoi(day);
	yearnum = atoi(year);
	
	if (daynum < 1 || daynum > 31) {
	  dprint1(3, "Error: day %d is obviously wrong! (valid_date)\n", 
	          daynum);
	  return(0);
	}
	
	if (yearnum < 1 || (yearnum > 100 && yearnum < 1900) ||
	    yearnum > 2000) {
	  dprint1(3, "Error: year %d is obviously wrong! (valid_date)\n", 
		yearnum);
	  return(0);
	}
	
	return(1);
}

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

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

	fix_time(entry->time);
}

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

	int hour, minute;

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

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

int
compare_dates(rec1, rec2)
struct header_rec *rec1, *rec2;
{
	/** This function works similarly to the "strcmp" function, but
	    has lots of knowledge about the internal date format...
	    Apologies to those who "know a better way"...
	**/

	int month1, day1, year1, hour1, minute1,
	    month2, day2, year2, hour2, minute2;

	year1 = atoi(rec1->year);
	year2 = atoi(rec2->year);

	if (year1 != year2)
	  return( year1 - year2 );

	/* And HERE's where the performance of this sort dies... */

	month1 = month_number(rec1->month);	/* retch...  gag....  */
	month2 = month_number(rec2->month);	/*    puke...         */

	if (month1 == -1) 
	  dprint1(2,"month_number failed on month '%s'\n", rec1->month);

	if (month2 == -1) 
	  dprint1(2,"month_number failed on month '%s'\n", rec2->month);

	if (month1 != month2)
	  return( month1 - month2 );

	/* back and cruisin' now, though... */

	day1 = atoi(rec1->day);	 /* unfortunately, 2 is greater than 19  */
	day2 = atoi(rec2->day);  /* on a dump string-only compare...     */

	if (day1 != day2)
	  return( day1 - day2 );

	/* we're really slowing down now... */

	sscanf(rec1->time, "%d:%d", &hour1, &minute1);
	sscanf(rec2->time, "%d:%d", &hour2, &minute2);

	if (hour1 != hour2)
	  return( hour1 - hour2 );

	return( day1 - day2 );		/* ignore seconds... */
}

int
month_number(name)
char *name;
{
	/** return the month number given the month name... **/

	char ch;

	switch (tolower(name[0])) {
	 case 'a' : if ((ch = tolower(name[1])) == 'p')	return(APRIL);
		    else if (ch == 'u') return(AUGUST);
		    else return(-1);	/* error! */
	
	 case 'd' : return(DECEMBER);
	 case 'f' : return(FEBRUARY);
	 case 'j' : if ((ch = tolower(name[1])) == 'a') return(JANUARY);
		    else if (ch == 'u') {
	              if ((ch = tolower(name[2])) == 'n') return(JUNE);
		      else if (ch == 'l') return(JULY);
		      else return(-1);		/* error! */
	            }
		    else return(-1);		/* error */
	 case 'm' : if ((ch = tolower(name[2])) == 'r') return(MARCH);
		    else if (ch == 'y') return(MAY);
		    else return(-1);		/* error! */
	 case 'n' : return(NOVEMBER);
	 case 'o' : return(OCTOBER);
	 case 's' : return(SEPTEMBER);
	 default  : return(-1);
	}
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 9538 ]
  then
    echo $filename changed - should be 9538 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

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

filename="src/delete.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/delete.c...
fi

cat << 'END-OF-FILE' > $filename
/**		delete.c		**/

/**  Delete or undelete files: just set flag in header record! 
     Also tags specified message(s)...

     (C) Copyright 1985  Dave Taylor
**/

#include "headers.h"

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

	if (real_del)
	  header_table[current-1].status |= DELETED;
	else if (ison(header_table[current-1].status, DELETED))
	  clearit(header_table[current-1].status, DELETED);
	else
	  setit(header_table[current-1].status, DELETED);

	show_msg_status(current-1);
}

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

	clearit(header_table[current-1].status, DELETED);

	show_msg_status(current-1);
}

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

	if (on_page(msg)) {
	  MoveCursor((msg % headers_per_page) + 4, 3);
	  Writechar( ison(header_table[msg].status, DELETED)? 'D' : ' ');
	}
}

tag_message()
{
	/** Tag current message.  If already tagged, untag it. **/

	if (ison(header_table[current-1].status, TAGGED))
	  clearit(header_table[current-1].status, TAGGED);
	else
	  setit(header_table[current-1].status, TAGGED);

	show_msg_tag(current-1);
}

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

	if (on_page(msg)) {
	  MoveCursor((msg % headers_per_page) + 4, 4);
	  Writechar( ison(header_table[msg].status, TAGGED)? '+' : ' ');
	}	
}

show_new_status(msg)
int msg;
{
	/** If the specified message is on this screen, show
	    the new status (could be marked for deletion now,
	    and could have tag removed...)
	**/

	if (on_page(msg)) 
	  PutLine2((msg % headers_per_page) + 4, 3, "%c%c",
		   ison(header_table[msg].status, DELETED)? 'D' : ' ',
		   ison(header_table[msg].status, TAGGED )? '+' : ' ');
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 1863 ]
  then
    echo $filename changed - should be 1863 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

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

filename="src/aliasdb.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/aliasdb.c...
fi

cat << 'END-OF-FILE' > $filename
/**			aliasdb.c			**/

/** Alias database files...

    (C) Copyright 1986 Dave Taylor
**/


#include "headers.h"

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

char *shift_lower(), *find_path_to();

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

	char   old_name[SLEN];
	char   address[SLEN];
	
	if (strlen(name) == 0)
	  return;
	
	strcpy(old_name, name);		/* save what we were given */

	if (expand_site(name, address) == -1) {
          if (display_error && name[0] != '!') {
	    dprint2(2,"Couldn't expand host %s in address. (%s)\n",
	             name, "findnode");
	    if (! check_only) {		/* be silent if just checking */
	      error1("Warning: couldn't expand %s...", name);
	      sleep(1);
	    }
	  }
	  strcpy(name, old_name);	/* and restore... */
	}
	else
	  strcpy(name, address);
	
	return;
}

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

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

#ifdef ACSNET

	strcpy(expanded, cryptic);	/* fast and simple */
	return(0);

#else

	char   name[VERY_LONG_STRING], sitename[VERY_LONG_STRING], 
               temp[VERY_LONG_STRING], old_name[VERY_LONG_STRING];
	char   *expand_domain(), *addr;
	register int i = 0, j = 0, domain_name;

	strcpy(old_name, cryptic);	/* remember what we were given */

	/** break down **/

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

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

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

	domain_name = (cryptic[i] == AT_SIGN);

	i++;

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

	name[j] = '\0';

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

#ifndef LOOK_CLOSE_AFTER_SEARCH

	if (talk_to(sitename)) {
	  strcpy(expanded, old_name);	/* restore! */
	  return(0);
	}
#endif

	if ((addr = find_path_to(sitename, TRUE)) == NULL) {

#ifdef LOOK_CLOSE_AFTER_SEARCH

	    if (talk_to(sitename)) {
	      strcpy(expanded, old_name);	/* restore! */
	      return(0);
	    }
	    else
#endif
	    if ((addr = expand_domain(cryptic)) != NULL) {
	       strcpy(expanded, addr);	/* into THIS buffer */
	       return(0);
	    }
	    else  if (size_of_pathfd == 0) {	/* no path database! */
	      strcpy(expanded, old_name);	/* restore! */
	      return(0);
	    }
	    else			     /* We just can't get there! */
	      return(-1);
	}
	else {			/* search succeeded */
	   sprintf(expanded, addr, name);
	   return(0);
	}
#endif
}

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

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

	address[0] = '\0';

	last = size_of_pathfd;

	do {

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

	  get_entry(machine, address, pathfd, middle);

	  compare = strcmp(name, machine);

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

	return(-1);
}

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

	fseek(fileid, offset, 0);

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

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

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

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

	struct stat buffer;

	if (stat(pathfile, &buffer) == -1) {
	  dprint2(1, "Warning: No pathalias file [filename %s] found! (%s)\n", 
		  pathfile, "init_findnode");
	  size_of_pathfd = 0;
	  return;
	}

	size_of_pathfd = (long) buffer.st_size;

	if ((pathfd = fopen(pathfile,"r")) == NULL) {
	  dprint2(1, "Warning: Can't read pathalias file [filename %s] (%s)\n", 
		   pathfile, "init_findnode");
	  size_of_pathfd = 0;
	}
	else
	  dprint2(2, "\nOpened file '%s' as path alias database.  (%s)\n\n", 
		  pathfile, "init_findnode");
}

char *find_path_to(machine, printf_format)
char *machine;
int   printf_format;
{
	/** Returns either the path to the specified machine or NULL if
	    not found.  If "printf_format" is TRUE, then it leaves the
	    '%s' intact, otherwise it assumes that the address is a uucp
	    address for the domain expansion program and removes the
	    last three characters of the expanded name ("!%s") since
	    they're redundant with the expansion!
        **/

	static char buffer[LONG_SLEN];	/* space for path */

	if (size_of_pathfd > 0)
	  if (binary_search(machine, buffer) != -1) {   	/* found it! */
	    if (! printf_format && strlen(buffer) > 3)
	      buffer[strlen(buffer)-3] = '\0';
	    return( (char *) buffer);
	  }

	return(NULL);			            /* failed if it's here! */
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 5484 ]
  then
    echo $filename changed - should be 5484 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

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

filename="src/file.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/file.c...
fi

cat << 'END-OF-FILE' > $filename
/**		file.c		**/

/** File I/O routines, mostly the save to file command...

    (C) Copyright 1986, Dave Taylor
**/

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

#ifdef BSD
#undef tolower
#endif

extern int errno;

char *error_name(), *error_description();

int
save()
{
	/** Save all tagged messages + current in a file.  If no messages
	    are tagged, save the current message instead!  This routine
	    will return ZERO if the operation failed.
	**/

	register int tagged = 0, i, oldstat, appending = 0;
	char filename[SLEN], address[LONG_SLEN], buffer[SLEN];
	FILE *save_file;

	oldstat = header_table[current-1].status;	/* remember */

	for (i=0; i < message_count; i++)
	  if (ison(header_table[i].status, TAGGED))
	    tagged++;

	if (tagged == 0) {
	  tagged = 1;
	  setit(header_table[current-1].status, TAGGED);
	}

	dprint2(4,"%d message%s tagged for saving (save)\n", tagged,
		plural(tagged));

	PutLine1(LINES-2, 0, "File message%s in: ", plural(tagged));

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

	if (tagged > 1)
	  optionally_enter(filename, LINES-2, 19, FALSE);
	else	
	  optionally_enter(filename, LINES-2, 18, FALSE);

	MoveCursor(LINES-1,0);

	if (strlen(filename) == 0) {  /** <return> means 'cancel', right? **/
	  header_table[current-1].status = oldstat;	/* BACK! */
	  return(0);
	}

	if (! expand_filename(filename)) {
	  dprint1(2,"Error: Failed on expansion of filename %s (save)\n", 
		   filename);
	  header_table[current-1].status = oldstat;	/* BACK! */
	  return(0);	/* failed expanding name! */
	}

	if (access(filename,ACCESS_EXISTS)) 	/* already there!! */
	  appending = 1;
	  
	if ((errno = can_open(filename, "a"))) {
	  error1("Wrong permissions to save message to file %s!", filename);
	  dprint2(2,"Error: access permission on file %s denied (%s)! (save)\n",
		  filename, error_name(errno));
	  header_table[current-1].status = oldstat;	/* BACK! */
	  return(0);
	}

	dprint1(4,"Saving mail to file '%s'...\n", filename);

	if ((save_file = fopen(filename,"a")) == NULL) {
	  dprint1(2, "Error: couldn't append to specified file %s (save)\n", 
		   filename);
	  error1("Couldn't append to file %s!", filename);
	  header_table[current-1].status = oldstat;	/* BACK! */
	  return(0); 
	}

	for (i=0; i < message_count; i++) 	/* save each tagged msg */
	  if (header_table[i].status & TAGGED)
	    save_message(i, filename, save_file, (tagged > 1), appending++);

	fclose(save_file);

	chown(filename, userid, groupid);	/* owned by user */
	if (tagged > 1)
	  error1("Message%s saved", plural(tagged));
	return(1);
}

int
save_message(number, filename, fd, pause, appending)
int number, pause, appending;
char *filename;
FILE *fd;
{
	/** Save an actual message to a file.  This is called by 
	    "save()" only!  The parameters are the message number,
	    and the name and file descriptor of the file to save to.
	    If 'pause' is true, a sleep(2) will be done after the
	    saved message appears on the screen...
	    'appending' is only true if the file already exists 
	**/

	register int save_current;
	
	dprint1(4, "\tSaving message %d to file...\n", number);

	save_current = current;
	current = number+1;
	copy_message("", fd, FALSE, FALSE);
	current = save_current;

	if (resolve_mode)
	  setit(header_table[number].status, DELETED); /* deleted, but ...   */
	clearit(header_table[number].status, TAGGED);  /* not tagged anymore */
	clearit(header_table[number].status, NEW);     /* it's not new now!  */

	if (! appending)	/* don't ask */
	  error2("Message %d appended to file %s", number+1, filename);
	else
	  error2("Message %d saved to file %s", number+1, filename);

	show_new_status(number);	/* update screen, if needed */

	if (pause) sleep(2);
}

int
expand_filename(filename)
char *filename;
{
	/** Expands '~' and '=' to specified file names, also will try to 
	    expand shell variables if encountered.. '+' and '%' are synonymous 
	    with '=' (folder dir)... **/

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

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

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

#ifdef SUN
	  env_value[0] = '\0';			/* null string for strlen! */
	  if (getenv(varname) != NULL)
#endif
	    strcpy(env_value, getenv(varname));

	  if (strlen(env_value) == 0) {
	    dprint2(3,"Error: Can't expand environment variable $%s (%s)\n",
		    varname, "expand_filename");
	    error1("Don't know what the value of $%s is!", varname);
	    return(0);
	  }

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

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 5704 ]
  then
    echo $filename changed - should be 5704 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

echo end of this archive file....
exit 0