[mod.sources] v09i011: ELM Mail System, Part11/19

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

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

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of archive 11 (of 19)."
# Contents:  src/screen.c src/screen3270.q src/showmsg.c src/strings.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"src/screen.c\" \(11166 characters\)
if test -f src/screen.c ; then 
  echo shar: Will not over-write existing file \"src/screen.c\"
else
sed "s/^X//" >src/screen.c <<'END_OF_src/screen.c'
X/**		screen.c		**/
X
X/**  screen display routines for ELM program 
X
X     (C) Copyright 1985, Dave Taylor
X**/
X
X#include "headers.h"
X
X#define  minimum(a,b)	((a) < (b) ? (a) : (b))
X
Xstatic   int  last_current	 = -1;
X
Xchar *strcpy(), *strncpy(), *nameof();
X
Xshowscreen()
X{
X	char buffer[SLEN];
X
X	ClearScreen();
X
X	if (selected)
X	  sprintf(buffer, 
X		"Mailbox is '%s' with %d shown out of %d [Elm %s]",
X	      nameof(infile), selected, message_count, VERSION);
X	else
X	  sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]",
X	      nameof(infile), message_count, plural(message_count), VERSION);
X	Centerline(1, buffer);
X
X	last_header_page = -1;	 	/* force a redraw regardless */
X	show_headers();
X
X	if (mini_menu)
X	  show_menu();
X
X	show_last_error();
X	
X	if (hp_terminal) 
X	  define_softkeys(MAIN);
X}
X
Xupdate_title()
X{
X	/** display a new title line, probably due to new mail arriving **/
X
X	char buffer[SLEN];
X
X	if (selected)
X	  sprintf(buffer, 
X		"Mailbox is '%s' with %d shown out of %d [Elm %s]",
X	      nameof(infile), selected, message_count, VERSION);
X	else
X	  sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]",
X	      nameof(infile), message_count, plural(message_count), VERSION);
X
X	ClearLine(1);
X
X	Centerline(1, buffer);
X}
X
Xshow_menu()
X{
X	/** write main system menu... **/
X
X	if (user_level == 0) {	/* a rank beginner.  Give less options  */
X	  Centerline(LINES-7,
X  "You can use any of the following commands by pressing the first character;");
X          Centerline(LINES-6,
X"D)elete mail,  M)ail a message,  R)eply to mail,  U)ndelete, or Q)uit");
X	  Centerline(LINES-5,
X  "To read a message, press <return>.  j = move arrow down, k = move arrow up");
X	} else {
X	Centerline(LINES-7,
X  "|=pipe, !=shell, ?=help, <n>=set current to n, /=search pattern");
X        Centerline(LINES-6,
X"A)lias, C)hange mailbox, D)elete, E)dit, F)orward, G)roup reply, M)ail,"); 
X	Centerline(LINES-5,
X  "N)ext, O)ptions, P)rint, R)eply, S)ave, T)ag, Q)uit, U)ndelete, or eX)it");
X	}
X}
X
Xint
Xshow_headers()
X{
X	/** Display page of headers (10) if present.  First check to 
X	    ensure that header_page is in bounds, fixing silently if not.
X	    If out of bounds, return zero, else return non-zero 
X	    Modified to only show headers that are "visible" to ze human
X	    person using ze program, eh?
X	**/
X
X	register int this_msg = 0, line = 4, last = 0, last_line, 
X		     displayed = 0;
X	char newfrom[SLEN], buffer[SLEN];
X	
X	if (fix_header_page()) {
X	  dprint0(7, "show_headers returned FALSE 'cause of fix-header-page\n");
X	  return(FALSE);
X	}
X
X	if (selected) {
X	  if ((header_page*headers_per_page) > selected) {
X	    dprint2(7, "show_headers returned FALSE since selected [%d] < %d\n",
X		    selected, header_page*headers_per_page);
X	    return(FALSE); 	/* too far! too far! */
X	  }
X
X	  dprint0(6,"** show_headers AND selected...\n");
X
X	  if (header_page == 0) {
X	    this_msg = visible_to_index(1);	
X	    displayed = 0;
X	  }
X	  else {
X	    this_msg = visible_to_index(header_page * headers_per_page + 1);
X	    displayed = header_page * headers_per_page;
X	  }
X
X	  dprint2(7,"this_msg (index) = %d  [header_page = %d]\n", this_msg, 
X		  header_page);
X
X	  dprint1(7,"we've already displayed %d messages\n", displayed);
X
X	  last = displayed+headers_per_page;
X
X	  dprint1(7,"and the last msg on this page is %d\n", last);
X	}
X	else {
X	  if (header_page == last_header_page) 	/* nothing to do! */
X	    return(FALSE);
X
X	  /** compute last header to display **/
X  
X	  this_msg = header_page * headers_per_page;
X	  last = this_msg + (headers_per_page - 1);
X	}
X
X	if (last >= message_count) last = message_count-1;
X
X	/** Okay, now let's show the header page! **/
X
X	ClearLine(line);	/* Clear the top line... */
X
X	MoveCursor(line, 0);	/* and move back to the top of the page... */
X
X	while ((selected && displayed < last) || this_msg <= last) {
X	  tail_of(header_table[this_msg].from, newfrom, TRUE); 
X
X	  if (selected) {
X	    if (this_msg == current-1) 
X	      build_header_line(buffer, &header_table[this_msg], ++displayed,
X			      TRUE, newfrom);
X	    else
X	      build_header_line(buffer, &header_table[this_msg], 
X			      ++displayed, FALSE, newfrom);
X	  } 
X	  else {
X	    if (this_msg == current-1) 
X	      build_header_line(buffer, &header_table[this_msg], this_msg+1, 
X		              TRUE, newfrom);
X	    else
X	      build_header_line(buffer, &header_table[this_msg], 
X			      this_msg+1, FALSE, newfrom);
X	  }
X
X	  Write_to_screen("%s\r\n", 1, buffer);	/* avoid '%' probs */
X	  CleartoEOLN();
X	  line++;		/* for clearing up in a sec... */
X
X	  if (selected) {
X	    if ((this_msg = next_visible(this_msg+1)-1) < 0)
X	      break;	/* GET OUTTA HERE! */
X
X	    /* the preceeding looks gross because we're using an INDEX
X	       variable to pretend to be a "current" counter, and the 
X	       current counter is always 1 greater than the actual 
X	       index.  Does that make sense??
X	     */
X	  }
X	  else
X	    this_msg++;					/* even dumber...  */
X	}
X
X	dprint0(1,"** out of redraw loop! **\n");
X
X	if (mini_menu)
X	  last_line = LINES-8;
X	else
X	  last_line = LINES-3;
X
X	while (line < last_line) {
X	  CleartoEOLN();
X	  Writechar('\r');
X	  Writechar('\n');
X	  line++;
X	}
X
X	display_central_message();
X
X	last_current = current;
X	last_header_page = header_page;
X
X	return(TRUE);
X}
X
Xshow_current()
X{
X	/** Show the new header, with all the usual checks **/
X
X	register int first = 0, last = 0, last_line, new_line, i=0, j=0;
X	char     newfrom[SLEN], old_buffer[SLEN], new_buffer[SLEN];
X
X	(void) fix_header_page();	/* Who cares what it does? ;-) */
X
X	/** compute last header to display **/
X
X	first = header_page * headers_per_page;
X	last  = first + (headers_per_page - 1);
X
X	if (last > message_count) 
X	  last = message_count;
X
X	/** okay, now let's show the pointers... **/
X
X	/** have we changed??? **/
X
X	if (current == last_current) 
X	  return;
X
X	if (selected) {
X	  dprint2(2,"\nshow_current\n* last_current = %d, current = %d\n", 
X			last_current, current);
X	  last_line = ((i=compute_visible(last_current-1)-1) %
X			 headers_per_page)+4;
X	  new_line  = ((j=compute_visible(current-1)-1) % headers_per_page)+4;
X	  dprint1(2,"* compute_visible(last-1)=%d\n", 
X		   compute_visible(last_current-1));
X	  dprint1(2,"* compute_visible(current-1)=%d\n", 
X		   compute_visible(current-1));
X	  dprint2(2,"* ending up with last_line = %d and new_line = %d\n",
X		   last_line, new_line);
X	}
X	else {
X	  last_line = ((last_current-1) % headers_per_page)+4;
X	  new_line  = ((current-1) % headers_per_page)+4;
X	}
X	
X	dprint4(7,
X	   "--> show-current: last_current=%d [%d] and current=%d [%d]\n",
X	   last_current, i, current, j);
X
X	dprint2(7,"    maps to lines %d and %d\n", last_line, new_line);
X
X	if (has_highlighting && ! arrow_cursor) {
X	  /** build the old and new header lines... **/
X  
X	  tail_of(header_table[current-1].from, newfrom, TRUE); 
X	  build_header_line(new_buffer, &header_table[current-1], 
X	         (selected? compute_visible(current-1) : current), 
X		  TRUE, newfrom);
X
X	  if (last_current > 0) {  /* say we went from no mail to new... */
X	    tail_of(header_table[last_current-1].from, newfrom, TRUE); 
X	    build_header_line(old_buffer, &header_table[last_current-1], 
X	         (selected? compute_visible(last_current-1) : last_current), 
X		  FALSE, newfrom);
X
X	    ClearLine(last_line);
X	    PutLine0(last_line, 0, old_buffer);
X	  }
X	  PutLine0(new_line, 0, new_buffer);
X	}
X	else {
X	  if (on_page(last_current)) 
X	    PutLine0(last_line,0,"  ");	/* remove old pointer... */
X	  if (on_page(current))
X	    PutLine0(new_line, 0,"->");
X	}
X	
X	last_current = current;
X}
X
Xbuild_header_line(buffer, entry, message_number, highlight, from)
Xchar *buffer;
Xstruct header_rec *entry;
Xint message_number, highlight;
Xchar *from;
X{
X	/** Build in buffer the message header ... entry is the current
X	    message entry, 'from' is a modified (displayable) from line, 
X	    'highlight' is either TRUE or FALSE, and 'message_number'
X	    is the number of the message.
X	**/
X
X	/** Note: using 'strncpy' allows us to output as much of the
X	    subject line as possible given the dimensions of the screen.
X	    The key is that 'strncpy' returns a 'char *' to the string
X	    that it is handing to the dummy variable!  Neat, eh? **/
X	
X	char subj[LONG_SLEN],		/* to output subject */
X	     buff[NLEN];		/* keep start_highlight value */
X
X	if (strcmp(start_highlight,"->") != 0 && arrow_cursor) {
X	  strcpy(buff, start_highlight);
X	  strcpy(start_highlight, "->");
X	}
X
X	strncpy(subj, entry->subject, COLUMNS-45);
X
X	subj[COLUMNS-45] = '\0';	/* insurance, eh? */
X
X	/* now THIS is a frightening format statement!!!  */
X
X	sprintf(buffer, "%s%s%c%c%c%-3d %3.3s %-2d %-18.18s (%d) %s%s%s", 
X		highlight? ((has_highlighting && !arrow_cursor) ?
X		           start_highlight : "->") : "  ",
X		(highlight && has_highlighting && !arrow_cursor)? "  " : "",
X		show_status(entry->status),
X		(entry->status & DELETED? 'D' : ' '), 
X		(entry->status & TAGGED?  '+' : ' '),
X	        message_number,
X	        entry->month, 
X		atoi(entry->day), 
X		from, 
X		entry->lines, 
X		(entry->lines / 1000   > 0? ""   :	/* spacing the  */
X		  entry->lines / 100   > 0? " "  :	/* same for the */
X		    entry->lines / 10  > 0? "  " :	/* lines in ()  */
X		                            "   "),    /*   [wierd]    */
X		subj,
X		(highlight && has_highlighting && !arrow_cursor) ?
X			  end_highlight : "");
X
X	/** Actually, it's rather an impressive feat that we can
X	    do so much in essentially one statement!  (Of course,
X	    I'll bet the test suite for the printf routine isn't
X	    THIS rigorous either!!!) (to be honest, though, just 
X	    looking at this statement makes me chuckle...)
X	**/
X
X	if (arrow_cursor) 			/* restore! */
X	  strcpy(start_highlight, buff);
X
X}
X
Xint
Xfix_header_page()
X{
X	/** this routine will check and ensure that the current header
X	    page being displayed contains messages!  It will silently
X	    fix 'header-page' if wrong.  Returns TRUE if changed.  **/
X
X	int last_page, old_header;
X
X	old_header = header_page;
X
X	last_page = (int) ((message_count-1) / headers_per_page);
X 
X	if (header_page > last_page) 
X	  header_page = last_page;
X	else if (header_page < 0) 
X          header_page = 0;
X
X	return(old_header != header_page);
X}
X
Xint
Xon_page(message)
Xint message;
X{
X	/** Returns true iff the specified message is on the displayed page. **/
X
X	dprint1(6,"** on_page(%d) returns...", message);
X
X	if (selected) message = compute_visible(message-1);
X
X	if (message >= header_page * headers_per_page)
X	  if (message <= ((header_page+1) * headers_per_page)) {
X	    dprint0(6,"TRUE\n");
X	    return(TRUE);
X	  }
X
X	dprint0(6,"FALSE\n");
X	return(FALSE);
X}
X
Xshow_status(status)
Xint status;
X{
X	/** This routine returns a single character indicative of
X	    the status of this message.  The precedence is;
X
X		F = form letter
X		E = Expired message
X	        P = Priority message
X		A = Action associated with message
X		N = New message
X		_ = (space) default 
X	**/
X
X	     if (status & FORM_LETTER) return( 'F' );
X	else if (status & EXPIRED)     return( 'E' );
X	else if (status & PRIORITY)    return( 'P' );
X	else if (status & ACTION)      return( 'A' );
X	else if (status & NEW)         return( 'N' );
X	else 			       return( ' ' );
X}
END_OF_src/screen.c
if test 11166 -ne `wc -c <src/screen.c`; then
    echo shar: \"src/screen.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/screen3270.q\" \(11593 characters\)
if test -f src/screen3270.q ; then 
  echo shar: Will not over-write existing file \"src/screen3270.q\"
else
sed "s/^X//" >src/screen3270.q <<'END_OF_src/screen3270.q'
XFrom hpccc!mcgregor@hplabs.ARPA Thu Jun  5 11:41:49 1986
XReceived: from hplabs.ARPA by hpldat ; Thu, 5 Jun 86 11:41:32 pdt
XMessage-Id: <8606051841.AA16872@hpldat>
XReceived: by hplabs.ARPA ; Thu, 5 Jun 86 11:38:07 pdt
XTo: hplabs!taylor@hplabs.ARPA
XDate: Thu, 5 Jun 86 11:36:39 PDT
XFrom: hpccc!mcgregor@hplabs.ARPA (Scott McGregor)
XSubject: revised screen3270.q
XTelephone: (415) 857-5875
XPostal-Address: Hewlett-Packard, PO Box 10301, Mail stop 20CH, Palo Alto CA 943X03-0890
XX-Mailer: ELM [version 1.0]
X
X
X/*
X * screen3270.q
X *
X *   Created for ELM, 5/86 to work (hopefully) on UTS systems with 3270
X *   type terminals, by Scott L. McGregor, HP Corporate Computing Center.
X *
X */
X
X#include "headers.h"
X#  include <sys/utsname.h>
X#  include <sys/tubio.h>
X
X#  include <errno.h>
X
X
X#  define  TTYIN		0		/* standard input */
X#include <stdio.h>
X#define MAXKEYS 101
X#define OFF 0
X#define UNKNOWN 0
X#define ON 1
X#define FALSE 0
X#define TRUE 1
X
Xchar *error_name();
X
Xextern int _line, _col;
X
X 
Xpfinitialize()
X{
X	char cp[80];
X	
X	dprint0(9,"pfinitialize()\n");
X	pfinit();
X	/*
X	 * load the system defined pfkeys
X	 */
X	pfload("/usr/local/etc/.elmpfrc");
X	/*
X	 * load the user's keys if any
X	 */
X	strcat(cp,home);
X	strcat(cp,"/.elmpfrc");
X	pfload(cp);
X	pfprint();
X}
X
X/*
X * note, inputs are limited to 80 characters! Any larger amount
X * will be truncated without notice!
X */
X
X
X/*
X * pfinit() initializes _pftable
X */
Xpfinit()
X{
X	int i,j;
X	
X	dprint0(9,"pfinit()\n");
X	for (i=0;i<MAXKEYS;i++) {
X		for (j=0;j<80;j++) {
X			_pftable[i][j]='\0';
X		}
X	}
X	return(0);
X}
X
X
X/*
X * pfset(key) sets _pftable entries.
X */
Xpfset(key,return_string)
Xint key;
Xchar *return_string;
X{
X	int i;
X
X	dprint2(9,"pfset(%d,%s)\n",key,return_string);
X	for (i=0;i<=80;i++) {
X		if (i <= strlen(return_string))
X			_pftable[key][i] = return_string[i];
X		else _pftable[key][i] = '\0';
X	}
X	dprint2(9,"pfset: %d %s\n",key,_pftable[key]);
X       
X	return(0);
X}
X
X/*
X *  pfload(name) reads file "name" and parses the
X *  key definitions in it into a table used by the
X *  pfreturn.
X */
Xpfload(name)
Xchar *name;
X{
X	FILE *pfdefs;
X	int i,j,k;
X	int key = 0;
X	char defn[80];
X	char newdefn[80];
X
X	dprint1(9,"pfload(%s)\n",name);
X	if ((pfdefs = fopen(name,"r")) == NULL){
X		dprint2(2,"%s pfrc open failed, %s \n",name,
X			error_name(errno));
X		return(0);
X	 }
X
X	 /*
X	  * This program reads .elmpfrc files which it currently
X	  * expects to be in the form:
X	  * 
X	  * <pfkeynumber> <whitespace> <pfkeyvalue> [<whitespace> <comment>]
X	  * 
X	  * Pfkeynumber is an integer 1-24.  Whitespace can be one
X	  * or more blanks or tabs.  Pfkeyvalue is any string NOT
X	  * containing whitespace (however, \b for blank, \t for tab
X	  * and \n for newline can be used instead to indicate that
X	  * the indicated whitespace character is part of a command.
X	  * Note that the EnTER key will NOT be treated as a newline
X	  * command, so defining a newline key is a good idea!
X	  * Anything else appearing on the line after the pfkey is ignored
X	  * and so can be used as a comment.
X	  *
X	  * This may not be the best form for a file used by
X	  * humans to set parms, and if someone comes up with a
X	  * better one and a parser to read it, then this can be
X	  * replaced.
X	  *
X	  */
X	 
X	dprint1(1,"%s pfrc opened\n",name);
X	k = 0;
X	while ( fscanf(pfdefs,"%d%s",&key,defn) != EOF ) {
X		dprint2(9,"pfkey definition 1: %d %s\n",key,defn);
X		if ((key < 0) || (key > MAXKEYS)) {
X			dprint2(9,"pfkey defn failed: key=%d MAXKEYS=%d\n",key,MAXKEYS);
X			k++;
X		} else {
X			dprint2(9,"pfkey definition 2: %d %s\n",key,defn);
X			for (i=0,j=0;i<strlen(defn);i++) {
X				if (defn[i]=='\\') {
X					if (defn[i+1]=='n') {
X						newdefn[j++]='\n'; i++;
X					}
X					if (defn[i+1]=='t') {
X						newdefn[j++]='\t'; i++;
X					}
X					if (defn[i+1]=='0') {
X						newdefn[j++]='\0'; i++;
X					}
X					if (defn[i+1]=='1') {
X						newdefn[j++]='\001'; i++;
X					}
X					if (defn[i+1]=='b') {
X						newdefn[j++]=' '; i++;
X					}
X				}
X				 else {
X					newdefn[j++]=defn[i];
X				}
X			}
X			dprint2(9,"pfkey new definition: %d %s\n",key,newdefn);
X			pfset(key,newdefn);
X		}
X	}
X	dprint1(9,"pfkey definition table:  %s\n",_pftable);
X	return(k);
X}
X
X
X/*
X * pfreturn(key) returns the stored string for that pfkey.
X */
Xpfreturn(key,string)
Xint key;
Xchar string[];
X{
X	int i;
X	
X	dprint2(9,"pfreturn(%d,%s)\n",key,string);
X	for (i=0;i<80;i++) {
X		string[i] = _pftable[key][i];
X	}
X	dprint1(9,"pfreturn string=%s\n",string);
X	return;
X}
X
X
X/*
X * pfprint() prints all pfkey definitions
X */
Xpfprint()
X
X{
X	int i;
X	char string[80];
X
X	for (i=0;i<MAXKEYS;i++) {
X		if (strlen(_pftable[i]) != 0)
X		dprint2(9,"%d pf table entry=%s\n",i+1,_pftable[i]);
X	}
X}
X
X/*
X * rowcol2offset(row,col) takes the row and column numbers and
X * returns the offset into the array.
X * row and column are assumed to vary from 0 to LINES-1, and COLUMNS-1
X * respectively.
X */
Xrowcol2offset(row,col)
Xint row, col;
X{
X	dprint2(9,"rowcol2offset(%d,%d)\n",row,col);
X	
X	if ((row <= LINES) && (row >= 0)) {
X		if ((col <= COLUMNS) && (col >=0)) {
X			return(row*COLUMNS+col);
X		}
X		else return(0);
X	}
X	else return(0);
X}
X
X/*
X * offset2row(offset) takes the offset returnes the row.
X * row is assumed to vary from 0 to LINES-1.
X */
Xoffset2row(offset)
Xint offset;
X{
X	int i;
X	
X	dprint1(9,"offset2row(%d)\n",offset);
X	i =  (int) (offset / COLUMNS);
X	dprint1(9,"offset2row returns= %d)\n",i);
X	return(i);
X}
X
X/*
X * offset2col(offset) takes the offset returnes the col.
X * col is assumed to vary from 0 to COLUMNS-1.
X */
Xoffset2col(offset)
Xint offset;
X{
X	int i;
X	
X	dprint1(9,"offset2col(%d)\n",offset);
X	i =  (int) (offset % COLUMNS);
X	dprint1(9,"offset2col returns= %d)\n",i);
X	return(i);
X}
X
X/*
X * Row(row) takes the row in 0 <= row < LINES and returns
X * row in 0 < row <= LINES.
X */
XRow(row)
Xint row;
X{
X	dprint1(9,"Row(%d)\n",row);
X	return(row+1);
X}
X
X/*
X * Col(Col) takes the col in 0 <= col < COLUMNS and returns
X * col in 0 < col <= COLUMNS.
X */
XCol(col)
Xint col;
X{
X	dprint1(9,"Col(%d)\n",col);
X	return(col+1);
X}
X
X
Xgethostname(hostname,size) /* get name of current host */
Xint size;
Xchar *hostname;
X{
X	/** Return the name of the current host machine.  UTS only **/
X
X	/** This routine compliments of Scott McGregor at the HP
X	    Corporate Computing Center **/
X     
X	int uname();
X	struct utsname name;
X
X        dprint2(9,"gethostname(%s,%d)\n",hostname,size);
X	(void) uname(&name);
X	(void) strncpy(hostname,name.nodename,size-1);
X	if (strlen(name.nodename) > SLEN)
X	  hostname[size] = '\0';
X}
X
Xint
Xisa3270()
X{
X	/** Returns TRUE and sets LINES and COLUMNS to the correct values
X	    for an Amdahl (IBM) tube screen, or returns FALSE if on a normal
X	    terminal (of course, next to a 3270, ANYTHING is normal!!) **/
X
X	struct tubiocb tubecb;
X	
X	dprint0(9,"isa3270()\n");
X	if (ioctl(TTYIN, TUBGETMOD, &tubecb) == -1){
X	  return(FALSE);	/* not a tube! */
X	}
X	LINES   = tubecb.line_cnt - 2;
X	COLUMNS = tubecb.col_cnt;
X	if (!check_only && !mail_only) {
X		isatube = TRUE;
X		return(TRUE);
X	}
X	 else {
X		 isatube = FALSE;
X		 return(FALSE);
X	}
X}
X
X/*
X * ReadCh3270() reads a character from the 3270.
X */
Xint ReadCh3270()
X{
X        /** read a character from the display! **/
X
X	register int x;
X	char tempstr[80];
X        char ch;
X	
X	dprint0(9,"ReadCh3270()\n");
X	if ((_input_buf_ptr > COLUMNS) ||
X        (_input_buffer[_input_buf_ptr] == '\0')) {
X		WriteScreen3270();
X		for (x=0; x < COLUMNS; x++) _input_buffer[x] = '\0';
X		panel (noerase, read) {
X			#@ LINES+1,1# #INC,_input_buffer,COLUMNS#
X		}
X		dprint1(9,"ReadCh3270 _input_buffer=%s\n",_input_buffer);
X		x=strlen(_input_buffer);
X		pfreturn(qskp,tempstr);
X		if (!strcmp(tempstr,"\001")) {
X			if (strlen(_input_buffer) == 1) {
X				tempstr[0]='\0';
X			}
X			 else {
X				tempstr[0]='\n';
X				tempstr[1]='\0';
X			}
X		}
X		dprint1(9,"ReadCh3270 pfkey=%s\n",tempstr);
X		strcat(_input_buffer,tempstr);
X		dprint1(9,"ReadCh3270 _input_buffer+pfkey=%s\n",_input_buffer);
X		ch = _input_buffer[0];
X		dprint1(9,"ReadCh3270 returns(%c)\n",ch);
X		_input_buf_ptr = 1;
X		return(ch);
X	}
X	 else {
X		 ch = _input_buffer[_input_buf_ptr];
X		 dprint1(9,"ReadCh3270 returns(%c)\n",ch);
X		 _input_buf_ptr = _input_buf_ptr + 1;
X		 return(ch);
X	}
X}
X
X
X/*
X * WriteScreen3270() Loads a screen to the buffer.
X *
X */
XWriteScreen3270()
X{
X	register int x;
X	int currcol;
X	int currrow;
X	int i;
X	int state = OFF;
X	int prevrow = 1;
X	int prevcol = 1;
X	int prevptr = 0;
X	int clear_state = ON;
X	char tempstr[80];
X	char copy_screen[66*132];
X	
X	dprint0(9,"WriteScreen3270()\n");
X	prevrow = 1;
X	prevcol = 1;
X	prevptr = 0;
X	state = OFF;
X	for (x=0; x < LINES*COLUMNS; x++){
X		if ((_internal_screen[x] == '\016')
X		&& (state == OFF)) {
X			currrow = (x / COLUMNS ) + 1;
X			currcol = (x % COLUMNS ) + 1 ;
X			i = x - prevptr - 1;
X			strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
X			panel(erase=clear_state,write,noread) {
X				#@ prevrow, prevcol # #ON,copy_screen,i #
X			}
X			clear_state = OFF;
X			state = ON;
X		     /* prevrow = currrow; */
X		     /* prevcol = currcol; */
X			prevrow = currrow + 1;
X			prevcol = 0;
X			prevptr = x+1;
X		}
X		else if ((_internal_screen[x] == '\017')
X		&& (state == ON)) {
X			currrow = (x / COLUMNS ) + 1;
X			currcol = (x % COLUMNS ) + 1;
X			i = x - prevptr - 1;
X			strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
X			panel(erase = clear_state,write,noread) {
X				#@ prevrow,prevcol # #OH,copy_screen,i #
X			}
X			clear_state = OFF;
X			state = OFF;
X		     /* prevrow = currrow; */
X		     /* prevcol = currcol; */
X			prevrow = currrow + 1;
X			prevcol = 0;
X		}
X	       else if (_internal_screen[x] < ' ') {
X			_internal_screen[x] = ' ';
X			prevptr = x + 1;
X	       }
X	}
X	/* write remainder of buffer */
X	if (state == OFF) {
X		currrow = (LINES) + 1 ;
X		currcol = (COLUMNS ) + 1;
X		i = x - prevptr  ;
X		strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
X		panel(erase=clear_state,write,noread) {
X			#@ prevrow,prevcol # #ON,copy_screen,i #
X		}
X	}
X	else {
X		currrow = (LINES ) + 1 ;
X		currcol = (COLUMNS ) + 1 ;
X		i = x - prevptr ;
X		strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
X		panel(erase=clear_state,write,noread) {
X			#@ prevrow,prevcol # #OH,copy_screen,i #
X		}
X	}
X}
X
X
X/*
X * Clear3270
X */
XClear3270()
X{
X	int  i,j;
X	
X	dprint0(9,"Clear3270()\n");
X	j = rowcol2offset(LINES,COLUMNS);
X	for (i = 0; i < j; i++) {
X		_internal_screen[i] = ' ';
X	}
X	return(0);
X}
X
X/*
X *  WriteChar3270(row,col) writes a character at the row and column.
X */
XWriteChar3270(row,col,ch)
Xint row, col;
Xchar ch;
X{
X	dprint3(9,"WriteChar3270(%d,%d,%c)\n",row,col,ch);
X	_internal_screen[rowcol2offset(row,col)] = ch;
X}
X
X/*
X *  WriteLine3270(row,col) writes a line at the row and column.
X */
XWriteLine3270(row,col,line)
Xint row, col;
Xchar *line;
X{
X	int i, j, k;
X	dprint3(9,"WriteLine3270(%d,%d,%s)\n",row,col,line);
X        _line = row;
X	_col = col;
X	k = strlen(line);
X	i=rowcol2offset(row,col);
X	for (j=0; j<k; i++, j++) {
X		if ((line[j] >= ' ')   ||
X		   (line[j] == '\016') ||
X		   (line[j] == '\017'))
X		_internal_screen[i] = line[j];
X		else _internal_screen[i] = ' ';
X	}
X   /*   _line = offset2row(i-1);  calling program updates location */
X   /*   _col  = offset2col(i-1); */
X	
X}
X
X
X/*
X * ClearEOLN3270() clears the remainder of the current line on a 3270.
X */
XClearEOLN3270()
X{
X	int i,j ;
X	
X	dprint0(9,"ClearEOLN3270()\n");
X	j = rowcol2offset(_line,COLUMNS);
X	for (i=rowcol2offset(_line,_col); i < j; i++) {
X		_internal_screen[i] = ' ';
X	}
X}
X
X/*
X * ClearEOS3270() clears the remainder of the current screen on a 3270.
X */
XClearEOS3270()
X{
X	int i,j;
X	
X	dprint0(9,"ClearEOS3270()\n");
X	j = rowcol2offset(LINES,COLUMNS);
X        for (i = rowcol2offset(_line,_col); i < j; i++) {
X		_internal_screen[i] = ' ';
X	}
X}
X
END_OF_src/screen3270.q
if test 11593 -ne `wc -c <src/screen3270.q`; then
    echo shar: \"src/screen3270.q\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/showmsg.c\" \(10629 characters\)
if test -f src/showmsg.c ; then 
  echo shar: Will not over-write existing file \"src/showmsg.c\"
else
sed "s/^X//" >src/showmsg.c <<'END_OF_src/showmsg.c'
X/** 			showmsg.c			**/
X
X/** This file contains all the routines needed to display the specified
X    message.
X
X   These routines (C) Copyright 1986 Dave Taylor 
X
X   Modified 6/86 to use pager variable!!!   Hurrah!!!!
X   Modified 7/86 to have secure pipes.. *sigh*
X**/
X
X#include "headers.h"
X#include <ctype.h>
X#include <errno.h>
X#include <signal.h>
X
X#ifdef BSD
X# include <sys/wait.h>
X# undef tolower
X#endif
X
Xextern int errno;
X
Xchar *error_name(), *strcat(), *strcpy();
Xvoid   _exit();
X
Xint    memory_lock = FALSE;	/* is it available?? */
Xint    pipe_abort  = FALSE;	/* did we receive a SIGNAL(SIGPIPE)? */
X
Xint
Xshow_msg(number)
Xint number;
X{
X	/*** display number'th message.  Get starting and ending lines
X	     of message from headers data structure, then fly through
X	     the file, displaying only those lines that are between the
X	     two!
X		Returns non-zero iff screen was changed
X	***/
X
X	dprint0(8, "show_msg called\n");
X
X	if (number > message_count) {
X	  error1("Only %d messages!", message_count);
X	  return(0);
X	}
X	else if (number < 1) {
X	  error("you can't read THAT message!");
X	  return(0);
X	}
X
X	clearit(header_table[number-1].status, NEW);   /* it's been read now! */
X
X	memory_lock = FALSE;
X
X	/* some explaination for that last one - We COULD use memory locking
X	   to speed up the paging, but the action of "ClearScreen" on a screen
X	   with memory lock turned on seems to vary considerably (amazingly so)
X	   so it's safer to only allow memory lock to be a viable bit of
X	   trickery when dumping text to the screen in scroll mode.
X	   Philosophical arguments should be forwarded to Bruce at the 
X	   University of Walamazoo, Australia, via ACSNet  *wry chuckle* */
X
X	return(show_message(header_table[number-1].lines, 
X	       header_table[number-1].offset,number));
X}
X
Xint
Xshow_message(lines, file_loc, msgnumber)
Xint lines, msgnumber;
Xlong file_loc;
X{
X	/*** Show the indicated range of lines from mailfile
X	     for message 'msgnumber' by using 'display'
X	     Returns non-zero iff screen was altered.
X	***/
X
X	dprint3(9,"show_message(%d,%ld,%d)\n", lines, file_loc, msgnumber);
X
X	if (fseek(mailfile, file_loc, 0) != 0) {
X	  dprint2(1,"Error: seek %d bytes into file, errno %s (show_message)\n",
X		  file_loc, error_name(errno));
X	  error2("ELM failed seeking %d bytes into file (%s)",
X		  file_loc, error_name(errno));	
X	  return(0);
X	}
X
X	if (feof(mailfile))
X	  dprint0(1,"\n*** seek put us at END OF FILE!!! ***\n");
X
X	/* next read will get 'this' line so must be at end of previous */
X
X	Raw(OFF);
X	if (strcmp(pager,"builtin") == 0 || strcmp(pager,"internal") == 0)
X	  display(lines, msgnumber);
X	else
X	  secure_display(lines, msgnumber);
X	Raw(ON);
X	if (memory_lock) EndMemlock();	/* turn it off!! */
X
X	return(1);	/* we did it boss! */
X}
X	
X
X/** This next one is the 'pipe' file descriptor for writing to the 
X    pager process... **/
X
XFILE   *output_pipe, *popen();
X
Xint
Xdisplay(lines, msgnum)
Xint lines, msgnum;
X{
X	/** Display specified number of lines from file mailfile.
X	    Note: This routine MUST be placed at the first line 
X	    of the input file! 
X	    Returns the same as the routine above (namely zero or one)
X	**/
X
X	char from_buffer[LONG_STRING], buffer[VERY_LONG_STRING], *full_month();
X
X	int lines_displayed = 0;	
X	int crypted = 0, gotten_key = 0;	/* encryption */
X	int weed_header, weeding_out = 0;	/* weeding    */ 
X	int mail_sent,				/* misc use   */
X	    form_letter = FALSE,		/* Form ltr?  */
X	    form_letter_section = 0,		/* section    */
X	    builtin = FALSE;			/* our pager? */
X
X	dprint3(4,"displaying %d lines from message %d using %s\n", 
X		lines, msgnum, pager);
X
X	ClearScreen();
X
X	if (cursor_control) transmit_functions(OFF);
X
X	pipe_abort = FALSE;
X
X	builtin = (strcmp(pager, "builtin") == 0 || 
X		   strcmp(pager,"internal") == 0);
X
X	if (form_letter = (header_table[msgnum-1].status&FORM_LETTER)) {
X	  if (filter)
X	    form_letter_section = 1;	/* initialize to section 1 */
X	}
X
X	if (builtin) 
X	  start_builtin(lines);
X	else {
X	  if ((output_pipe = popen(pager,"w")) == NULL) {
X	    error2("Can't create pipe to %s [%s]", pager, 
X		    error_name(errno));
X	    dprint2(1,"\n*** Can't create pipe to %s - error %s ***\n\n",
X	   	    pager, error_name(errno));
X	    return(0);
X	  }
X	  dprint1(4,"Opened a write-only pipe to routine %s \n", pager);
X	}
X
X	if (title_messages) {
X
X	  mail_sent = (strncmp(header_table[msgnum-1].from, "To:", 3) == 0);
X
X	  tail_of(header_table[msgnum-1].from, from_buffer, FALSE);
X
X	  sprintf(buffer, "\r%s #%d %s %s%s\t %s %s %s, %d at %s%s\n\r",
X		   form_letter? "Form": "Message",
X		    msgnum, mail_sent? "to" : "from", from_buffer,
X		   (strlen(from_buffer) > 24? "\n\r": 
X		     (strlen(from_buffer) > 16 ? "" : "\t")),
X		   "Mailed",
X     		   full_month(header_table[msgnum-1].month), 
X		   header_table[msgnum-1].day, 
X	           atoi(header_table[msgnum-1].year) + 1900,
X	           header_table[msgnum-1].time,
X		   filter? "": "\n\r\n\r");
X
X	  if (builtin)
X	    display_line(buffer);
X	  else
X	    fprintf(output_pipe, "%s", buffer);
X
X	  if (! mail_sent && matches_weedlist("To:") && filter &&
X	      strcmp(header_table[current-1].to,username) != 0 &&
X	      strlen(header_table[current-1].to) > 0) {
X	    sprintf(buffer, "\n\r(message addressed to %s)\n\r", 
X		    header_table[current-1].to);
X	    if (builtin)
X	      display_line(buffer);
X	    else
X	      fprintf(output_pipe, "%s", buffer);
X	  }
X	
X	  /** The test above is: if we didn't originally send the mail
X	      (e.g. we're not reading "mail.sent") AND the user is currently
X	      weeding out the "To:" line (otherwise they'll get it twice!)
X	      AND the user is actually weeding out headers AND the message 
X	      wasn't addressed to the user AND the 'to' address is non-zero 
X	      (consider what happens when the message doesn't HAVE a "To:" 
X	      line...the value is NULL but it doesn't match the username 
X	      either.  We don't want to display something ugly like 
X	      "(message addressed to )" which will just clutter up the 
X	      display!).
X
X	      And you thought programming was EASY!!!!
X	  **/
X	}
X
X	weed_header = filter;	/* allow us to change it after header */
X
X	while (lines > 0 && pipe_abort == FALSE) {
X
X	    if (fgets(buffer, VERY_LONG_STRING, mailfile) == NULL) {
X	      if (lines_displayed == 0) {
X
X		/* AUGH!  Why do we get this occasionally???  */
X
X	        dprint0(1,
X	   	  "\n\n** Out of Sync!!  EOF with nothing read (display) **\n");
X		dprint0(1,"** closing and reopening mailfile... **\n\n");
X
X	        if (!builtin) pclose(output_pipe);	/* close pipe NOW! */
X
X		if (mailfile != NULL)
X		  fclose(mailfile);		/* huh? */
X
X		if ((mailfile = fopen(infile, "r")) == NULL) {
X		  error("Sync error: can't re-open mailbox!!");
X		  show_mailfile_stats();
X		  emergency_exit();
X	        }
X	        return(show_message(lines, 
X			            header_table[msgnum-1].offset,
X				    msgnum));
X	      }
X	      if (!builtin) 
X	        pclose(output_pipe);
X	      if (lines == 0 && pipe_abort == FALSE) {	/* displayed it all */
X		if (!builtin)
X	          PutLine0(LINES,0,"\rPress <return> to return to Elm: ");
X	        else
X		  printf("\n\r\n\rPress <return> to return to Elm: ");
X		fflush(stdout);
X	        Raw(ON);
X	        (void) ReadCh();
X	        Raw(OFF);
X	      }
X	      return(TRUE);
X	    }
X
X	    if (strlen(buffer) > 0) 
X              no_ret(buffer);
X
X	    if (strlen(buffer) == 0) {
X	      weed_header = 0;		/* past header! */
X	      weeding_out = 0;
X	    }
X
X	    lines--;
X	    lines_displayed++;
X
X	    if (form_letter && weed_header)
X		/* skip it.  NEVER display random headers in forms! */;
X	    else if (weed_header && matches_weedlist(buffer)) 
X	      weeding_out = 1;	 /* aha!  We don't want to see this! */
X	    else if (buffer[0] == '[') {
X	      if (strcmp(buffer, START_ENCODE)==0)
X	        crypted++;
X	      else if (strcmp(buffer, END_ENCODE)==0)
X	        crypted--;
X	      else if (crypted) {
X                encode(buffer);
X	        show_line(buffer, builtin);
X	      }
X	      else
X	        show_line(buffer, builtin);
X	    } 
X	    else if (crypted) {
X	      if (! gotten_key++) getkey(OFF);
X	      encode(buffer);
X	      show_line(buffer, builtin); 
X	    }
X	    else if (weeding_out) {
X	      weeding_out = (whitespace(buffer[0]));	/* 'n' line weed */
X	      if (! weeding_out) 	/* just turned on! */
X	        show_line(buffer, builtin);
X	    } 
X	    else if (form_letter && first_word(buffer,"***") && filter) {
X	      strcpy(buffer,
X"\n------------------------------------------------------------------------------\n");
X	      show_line(buffer, builtin);	/* hide '***' */
X	      form_letter_section++;
X	    }
X	    else if (form_letter_section == 1 || form_letter_section == 3)
X	      /** skip this stuff - we can't deal with it... **/;
X	    else
X	      show_line(buffer, builtin);
X	}
X
X        if (cursor_control) transmit_functions(ON);
X
X	if (! builtin) pclose(output_pipe);
X
X	if (lines == 0 && pipe_abort == FALSE) {  	/* displayed it all! */
X	  if (! builtin)
X	    PutLine0(LINES,0,"\rPress <return> to return to Elm: ");
X	  else
X	    printf("\n\r\n\rPress <return> to return to Elm: ");
X	  fflush(stdout);
X	  Raw(ON);
X	  (void) ReadCh();
X	  Raw(OFF);
X	}
X	return(TRUE);
X}
X
Xshow_line(buffer, builtin)
Xchar *buffer;
Xint  builtin;
X{
X	/** Hands the given line to the output pipe.  'builtin' is true if
X	    we're using the builtin pager.  **/ 
X	
X	if (builtin) {
X	  strcat(buffer, "\n\r");
X	  pipe_abort = display_line(buffer);
X	}
X	else {
X	  errno = 0;
X	  fprintf(output_pipe, "%s\n", buffer);
X	
X	  if (errno != 0)
X	    dprint1(1,"\terror %s hit!\n", error_name(errno));
X	}
X}
X
Xint
Xsecure_display(lines, msgnumber)
Xint lines, msgnumber;
X{
X	/** This is the cheap way to implement secure pipes - spawn a
X	    child process running under the old userid, then open the
X	    pager and feed the message to it.  When the subprocess ends
X	    (the routine returns) simply return.  Simple and effective.
X	    (too bad it's this much of a hassle to implement secure
X	    pipes, though - I can imagine it being a constant problem!)
X	**/
X
X	int pid, w;
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
X	  setgid(groupid);	/* and group id		    */
X	  setuid(userid);	/* back to the normal user! */
X
X	  _exit(display(lines, msgnumber));
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	signal(SIGINT, istat);
X	signal(SIGQUIT, qstat);
X
X#ifdef BSD
X	return(status.w_retcode);
X#else
X	return(status);
X#endif
X}
END_OF_src/showmsg.c
if test 10629 -ne `wc -c <src/showmsg.c`; then
    echo shar: \"src/showmsg.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/strings.c\" \(11005 characters\)
if test -f src/strings.c ; then 
  echo shar: Will not over-write existing file \"src/strings.c\"
else
sed "s/^X//" >src/strings.c <<'END_OF_src/strings.c'
X/**			strings.c		**/
X
X/** This file contains all the string oriented functions for the
X    ELM Mailer, and lots of other generally useful string functions! 
X
X    For BSD systems, this file also includes the function "tolower"
X    to translate the given character from upper case to lower case.
X
X    (C) Copyright 1985, Dave Taylor
X**/
X
X#include <stdio.h>
X#include "headers.h"
X#include <ctype.h>
X
X#ifdef BSD
X#undef tolower
X#undef toupper
X#endif
X
X/** forward declarations **/
X
Xchar *format_long(), *strip_commas(), *tail_of_string(), *shift_lower(),
X     *get_token(), *strip_parens(), *argv_zero(), *strcpy(), *strncpy();
X
X#ifdef BSD
X
Xint
Xtolower(ch)
Xchar ch;
X{
X	/** This should be a macro call, but if you use this as a macro
X	    calls to 'tolower' where the argument is a function call will
X	    cause the function to be called TWICE which is obviously the
X	    wrong behaviour.  On the other hand, to just blindly translate
X	    assuming the character is always uppercase can cause BIG
X	    problems, so...
X	**/
X
X	return ( isupper(ch) ? ch - 'A' + 'a' : ch );
X}
X
Xint
Xtoupper(ch)
Xchar ch;
X{
X	/** see comment for above routine - tolower() **/
X
X	return ( islower(ch) ? ch - 'a' + 'A' : ch );
X}
X
X#endif
X
Xint
Xprintable_chars(string)
Xchar *string;
X{
X	/** Returns the number of "printable" (ie non-control) characters
X	    in the given string... Modified 4/86 to know about TAB
X	    characters being every eight characters... **/
X
X	register int count = 0, index;
X
X	for (index = 0; index < strlen(string); index++)
X	  if (string[index] >= ' ') 
X	    if (string[index] == '\t')
X	      count += (7-(count % 8));
X	    else
X	      count++;
X
X	return(count);
X}
X
Xtail_of(from, buffer, header_line)
Xchar *from, *buffer;
Xint   header_line;
X{
X	/** Return last two words of 'from'.  This is to allow
X	    painless display of long return addresses as simply the
X	    machine!username.  Alternatively, if the first three
X	    characters of the 'from' address are 'To:' and 'header_line'
X	    is TRUE, then return the buffer value prepended with 'To '. 
X
X	    Mangled to know about the PREFER_UUCP hack.  6/86
X	**/
X
X	/** Note: '!' delimits Usenet nodes, '@' delimits ARPA nodes,
X	          ':' delimits CSNet & Bitnet nodes, and '%' delimits
X		  multiple stage ARPA hops... **/
X
X	register int loc, i = 0, cnt = 0;
X	char     tempbuffer[SLEN];
X	
X#ifdef PREFER_UUCP
X	
X	/** let's see if we have an address appropriate for hacking **/
X
X	if (chloc(from,'!') != -1 && in_string(from, BOGUS_INTERNET))
X	   from[strlen(from)-strlen(BOGUS_INTERNET)] = '\0';
X
X#endif
X
X	for (loc = strlen(from)-1; loc >= 0 && cnt < 2; loc--) {
X	  if (from[loc] == BANG || from[loc] == AT_SIGN ||
X	      from[loc] == COLON) cnt++;
X	  if (cnt < 2) buffer[i++] = from[loc];
X	}
X
X	buffer[i] = '\0';
X
X	reverse(buffer);
X
X	if ((strncmp(buffer,"To:", 3) == 0) && header_line)
X	  buffer[2] = ' ';
X	else if ((strncmp(from, "To:", 3) == 0) && header_line) {
X	  sprintf(tempbuffer,"To %s", buffer); 
X	  strcpy(buffer, tempbuffer);
X	}
X	else if (strncmp(buffer, "To:", 3) == 0) {
X	  for (i=3; i < strlen(buffer); i++)
X	    tempbuffer[i-3] = buffer[i];
X	  tempbuffer[i-3] = '\0';
X	  strcpy(buffer, tempbuffer);
X	}
X}
X
Xchar *format_long(inbuff, init_len)
Xchar *inbuff;
Xint   init_len;
X{
X	/** Return buffer with \n\t sequences added at each point where it 
X	    would be more than 80 chars long.  It only allows the breaks at 
X	    legal points (ie commas followed by white spaces).  init-len is 
X	    the characters already on the first line...  Changed so that if 
X            this is called while mailing without the overhead of "elm", it'll 
X            include "\r\n\t" instead.
X	    Changed to use ',' as a separator and to REPLACE it after it's
X	    found in the output stream...
X	**/
X
X	static char ret_buffer[VERY_LONG_STRING];
X	register int index = 0, current_length = 0, depth=15, i;
X	char     buffer[VERY_LONG_STRING];
X	char     *word, *bufptr;
X
X	strcpy(buffer, inbuff);
X
X	bufptr = (char *) buffer;
X
X	current_length = init_len + 2;	/* for luck */
X
X	while ((word = get_token(bufptr,",", depth)) != NULL) {
X
X	    /* first, decide what sort of separator we need, if any... */
X
X	  if (strlen(word) + current_length > 80) {
X	    if (index > 0) {
X	      ret_buffer[index++] = ',';	/* close 'er up, doctor! */
X	      if (mail_only)
X	        ret_buffer[index++] = '\r';
X	      ret_buffer[index++] = '\n';
X	      ret_buffer[index++] = '\t';
X	    }
X	    
X	    /* now add this pup! */
X
X	    for (i=(word[0] == ' '? 1:0); i<strlen(word); i++)
X	      ret_buffer[index++] = word[i];
X	    current_length = strlen(word) + 8;	/* 8 = TAB */
X	  }
X
X	  else {	/* just add this address to the list.. */
X
X	    if (index > 0) {
X	      ret_buffer[index++] = ',';	/* comma added! */
X	      ret_buffer[index++] = ' ';
X	      current_length += 2;
X	    }
X	    for (i=(word[0] == ' '? 1:0); i<strlen(word); i++)
X	      ret_buffer[index++] = word[i];
X	    current_length += strlen(word);
X	  }
X	
X	  bufptr = NULL;
X	}
X	
X	ret_buffer[index] = '\0';
X
X	return( (char *) ret_buffer);
X}
X
Xchar *strip_commas(string)
Xchar *string;
X{
X	/** return string with all commas changed to spaces.  This IS
X	    destructive and will permanently change the input string.. **/
X
X	register int i;
X
X	for (i=0; i < strlen(string); i++)
X	  if (string[i] == COMMA)
X	    string[i] = SPACE;
X
X	return( (char *) string);
X}
X
Xchar *strip_parens(string)
Xchar *string;
X{
X	/** Return string with all parenthesized information removed.
X	    This is a non-destructive algorithm... **/
X
X	static char  buffer[VERY_LONG_STRING];
X	register int i, depth = 0, buffer_index = 0;
X
X	for (i=0; i < strlen(string); i++) {
X	  if (string[i] == '(')
X	    depth++;
X	  else if (string[i] == ')') 
X	    depth--;
X	  else if (depth == 0)
X	    buffer[buffer_index++] = string[i];
X	}
X	
X	buffer[buffer_index] = '\0';
X
X	return( (char *) buffer);
X}
X
Xmove_left(string, chars)
Xchar string[];
Xint  chars;
X{
X	/** moves string chars characters to the left DESTRUCTIVELY **/
X
X	register int i;
X
X	chars--; /* index starting at zero! */
X
X	for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
X	  string[i-chars] = string[i];
X
X	string[i-chars] = '\0';
X}
X
Xremove_first_word(string)
Xchar *string;
X{	/** removes first word of string, ie up to first non-white space
X	    following a white space! **/
X
X	register int loc;
X
X	for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
X	    ;
X
X	while (string[loc] == ' ' || string[loc] == '\t')
X	  loc++;
X	
X	move_left(string, ++loc);
X}
X
Xsplit_word(buffer, first, rest)
Xchar *buffer, *first, *rest;
X{
X	/** Rip the buffer into first word and rest of word, translating it
X	    all to lower case as we go along..
X	**/
X
X	register int i, j = 0;
X
X	/** skip leading white space, just in case.. **/
X
X	for (i=0; whitespace(buffer[i]); i++)	;
X
X	/** now copy into 'first' until we hit white space or EOLN **/
X
X	for (j=0; i < strlen(buffer) && ! whitespace(buffer[i]); )
X	  first[j++] = tolower(buffer[i++]);
X
X	first[j] = '\0';
X	
X	while (whitespace(buffer[i])) i++;
X
X	for (j=0; i < strlen(buffer); i++)
X	  rest[j++] = tolower(buffer[i]);
X
X	rest[j] = '\0';
X
X	return;
X}
X
Xchar *tail_of_string(string, maxchars)
Xchar *string;
Xint  maxchars;
X{
X	/** Return a string that is the last 'maxchars' characters of the
X	    given string.  This is only used if the first word of the string
X	    is longer than maxchars, else it will return what is given to
X	    it... 
X	**/
X
X	static char buffer[SLEN];
X	register int index, i;
X
X	for (index=0;! whitespace(string[index]) && index < strlen(string); 
X	     index++)
X	  ;
X
X	if (index < maxchars) {
X	  strncpy(buffer, string, maxchars-2);	/* word too short */
X	  buffer[maxchars-2] = '.';
X	  buffer[maxchars-1] = '.';
X	  buffer[maxchars]   = '.';
X	  buffer[maxchars+1] = '\0';
X	} 
X	else {
X	  i = maxchars;
X	  buffer[i--] = '\0';
X	  while (i > 1) 
X	    buffer[i--] = string[index--];
X	  buffer[2] = '.';
X	  buffer[1] = '.';
X	  buffer[0] = '.';
X	}
X
X	return( (char *) buffer);
X}
X
Xreverse(string)
Xchar *string;
X{
X	/** reverse string... pretty trivial routine, actually! **/
X
X	char buffer[SLEN];
X	register int i, j = 0;
X
X	for (i = strlen(string)-1; i >= 0; i--)
X	  buffer[j++] = string[i];
X
X	buffer[j] = '\0';
X
X	strcpy(string, buffer);
X}
X
Xint
Xget_word(buffer, start, word)
Xchar *buffer, *word;
Xint start;
X{
X	/**	return next word in buffer, starting at 'start'.
X		delimiter is space or end-of-line.  Returns the
X		location of the next word, or -1 if returning
X		the last word in the buffer.  -2 indicates empty
X		buffer!  **/
X
X	register int loc = 0;
X
X	while (buffer[start] == ' ' && buffer[start] != '\0')
X	  start++;
X
X	if (buffer[start] == '\0') return(-2);	 /* nothing IN buffer! */
X
X	while (buffer[start] != ' ' && buffer[start] != '\0')
X	  word[loc++] = buffer[start++];
X
X	word[loc] = '\0';
X	return(start);
X}
X
Xchar *shift_lower(string)
Xchar *string;
X{
X	/** return 'string' shifted to lower case.  Do NOT touch the
X	    actual string handed to us! **/
X
X	static char buffer[LONG_SLEN];
X	register int i;
X
X	for (i=0; i < strlen(string); i++)
X	  if (isupper(string[i]))
X	    buffer[i] = tolower(string[i]);
X	  else
X	    buffer[i] = string[i];
X	
X	buffer[strlen(string)] = 0;
X	
X	return( (char *) buffer);
X}
X
XCenterline(line, string)
Xint line;
Xchar *string;
X{
X	/** Output 'string' on the given line, centered. **/
X
X	register int length, col;
X
X	length = strlen(string);
X
X	if (length > COLUMNS)
X	  col = 0;
X	else
X	  col = (COLUMNS - length) / 2;
X
X	PutLine0(line, col, string);
X}
X
Xchar *argv_zero(string)
Xchar *string;
X{
X	/** given a string of the form "/something/name" return a
X	    string of the form "name"... **/
X
X	static char buffer[NLEN];
X	register int i, j=0;
X
X	for (i=strlen(string)-1; string[i] != '/'; i--)
X	  buffer[j++] = string[i];
X	buffer[j] = '\0';
X
X	reverse(buffer);
X
X	return( (char *) buffer);
X}
X
X#define MAX_RECURSION		20		/* up to 20 deep recursion */
X
Xchar *get_token(source, keys, depth)
Xchar *source, *keys;
Xint   depth;
X{
X	/** This function is similar to strtok() (see "opt_utils")
X	    but allows nesting of calls via pointers... 
X	**/
X
X	register int  last_ch;
X	static   char *buffers[MAX_RECURSION];
X	char     *return_value, *sourceptr;
X
X	if (depth > MAX_RECURSION) {
X	   error1("get_token calls nested greater than %d deep!", 
X		  MAX_RECURSION);
X	   emergency_exit();
X	}
X
X	if (source != NULL)
X	  buffers[depth] = source;
X	
X	sourceptr = buffers[depth];
X	
X	if (*sourceptr == '\0') 
X	  return(NULL);		/* we hit end-of-string last time!? */
X
X	sourceptr += strspn(sourceptr, keys);	  /* skip the bad.. */
X	
X	if (*sourceptr == '\0') {
X	  buffers[depth] = sourceptr;
X	  return(NULL);			/* we've hit end-of-string   */
X	}
X
X	last_ch = strcspn(sourceptr, keys);   /* end of good stuff   */
X
X	return_value = sourceptr;	      /* and get the ret     */
X
X	sourceptr += last_ch;		      /* ...value            */
X
X	if (*sourceptr != '\0')		/** don't forget if we're at end! **/
X	  sourceptr++;			      
X	
X	return_value[last_ch] = '\0';	      /* ..ending right      */
X
X	buffers[depth] = sourceptr;	      /* save this, mate!    */
X
X	return((char *) return_value);	     /* and we're outta here! */
X}
END_OF_src/strings.c
if test 11005 -ne `wc -c <src/strings.c`; then
    echo shar: \"src/strings.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: End of archive 11 \(of 19\).
cp /dev/null ark11isdone
DONE=true
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	echo shar: You still need to run archive ${I}.
	DONE=false
    fi
done
if test "$DONE" = "true" ; then
	echo You have unpacked all 19 archives.
	echo "See the Instructions file"
	rm -f ark[1-9]isdone ark[1-9][0-9]isdone
fi
##  End of shell archive.
exit 0