wht@n4hgf.uucp (Warren Tucker) (01/06/91)
Submitted-by: wht@n4hgf.uucp (Warren Tucker) Posting-number: Volume 16, Issue 48 Archive-name: ecu3/part24 ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is part 24 of ecu3 if touch 2>&1 | fgrep 'amc' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= sea/scurses.c ============== if test ! -d 'sea'; then echo 'x - creating directory sea' mkdir 'sea' fi echo 'x - extracting sea/scurses.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'sea/scurses.c' && X/*+------------------------------------------------------------------------- X scurses.c -- ecu file transfer program curses interface X X 000000000011111111112222222222333333333344444444445555555550 X 012345678901234567890123456789012345678901234567890123456789 X00.----------------------------------------------------------. X01| SEAlink_6____ _39____________________________________ | X02| File ### of ###: _38__________________________________ | X03| File position: _8______ length: _8______ | X04| _55____________________________________________________ | transaction X05| _55____________________________________________________ | last rx/tx hdr X06| Comm I/O: rx _8______ tx _8______ bytes | X07| Baud rate: _5___ BINARY blklen: _____ comm mode: CBREAK | X08| Time started: session: HH:MM:SS this file: HH:MM:SS | X09| elpased: HH:MM:SS time: HH:MM:SS | X10| Error counts: this file: _4__ total: _6____ | X11| _55____________________________________________________ | err str X12| _55____________________________________________________ | comment str X13| _55____________________________________________________ | remote info X14`----------------------------------------------------------' X X Defined functions: X clear_area(win,row,col,len) X clear_area_char(win,row,col,len,fillchar) X get_elapsed_time(elapsed_seconds) X get_tod(type,tod) X report_error_count() X report_file_byte_io(count) X report_file_close() X report_file_open_length(length) X report_file_open_tod() X report_file_rcv_started(filename,length,last_mod_time) X report_file_send_open(filename,filestat) X report_init(title) X report_last_rxhdr(rptstr,error_flag) X report_last_txhdr(rptstr,error_flag) X report_line(baud_rate,mode) X report_protocol_crc_type(str) X report_rx_ind(status) X report_rx_tx_count() X report_rxpos(rxpos) X report_str(rptstr,error_flag) X report_top_line(topstr) X report_transaction(str) X report_tx_ind(status) X report_txpos(txpos) X report_uninit() X X------------------------------------------------------------------------*/ X/*+:EDITS:*/ X/*:09-19-1990-19:36-wht@n4hgf-ecu_log_event now gets pid for log from caller */ X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */ X X#include <curses.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <ctype.h> X#include <signal.h> X#include <time.h> X#include <sys/timeb.h> X#include <sys/machdep.h> X X#include "../pc_scr.h" X#include "lint_args.h" X Xlong time(); Xextern char *tzname[]; Xstruct tm *localtime(); X X X#define WIN_LINES 15 X#define WIN_COLS 60 X#define WIN_TOPY 2 X#define WIN_LEFTX 8 X Xextern char curr_dir[]; Xextern char s128[]; Xextern int Filcnt; Xextern int Restricted; Xextern int sending_flag; /* send == 1, receive == 0 */ Xextern int npaths; Xextern long rxpos; Xextern int log_packets; X XWINDOW *win; Xint (*original_sigint_handler)(); Xint (*original_sigquit_handler)(); Xint (*original_sigterm_handler)(); Xint curses_installed = 0; /* curses not yet active */ Xint this_file_errors = 0; Xint total_errors = 0; Xlong current_seconds; Xlong start_seconds; Xlong elapsed_seconds; Xunsigned long total_data_chars_xfered = 0L; X Xunsigned char sTL = at_TL; Xunsigned char sTR = at_TR; Xunsigned char sBL = at_BL; Xunsigned char sBR = at_BR; Xunsigned char sLT = at_LT; Xunsigned char sRT = at_RT; Xunsigned char sVR = at_VR; Xunsigned char sHR = at_HR; X Xchar *win_template[] = X{ X/*00000000001111111111222222222233333333334444444444555555555 */ X/*01234567890123456789012345678901234567890123456789012345678 */ X/*.----------------------------------------------------------. */ X " SEAlink ", /* 1 */ X " File ### of ###: _____________________________________ ", /* 2 */ X " File position: ________ length: ________ ", /* 3 */ X " ", /* 4 */ X " tx: ______________________ rx: ______________________ ", /* 5 */ X " Comm I/O: rx ________ tx ________ bytes ", /* 6 */ X " Baud rate: _____ BINARY blklen: 128 comm mode: ______ ", /* 7 */ X " Time started: session: --:--:-- this file: --:--:-- ", /* 8 */ X " elapsed: --:--:-- current: --:--:-- ", /* 9 */ X " Error counts: this file: ____ total: ______ ", /* 10 */ X " ", /* 11 */ X " ", /* 12 */ X " ", /* 13 */ X/*`----------------------------------------------------------' */ X(char *)0 X}; X X/*+----------------------------------------------------------------------- X char *get_elapsed_time(elapsed_seconds) X hh:mm:ss returned X static string address is returned X------------------------------------------------------------------------*/ Xchar *get_elapsed_time(elapsed_seconds) Xlong elapsed_seconds; X{ X static char elapsed_time_str[10]; X long hh,mm,ss; X X hh = elapsed_seconds / 3600; X elapsed_seconds -= hh * 3600; X mm = elapsed_seconds / 60L; X elapsed_seconds -= mm * 60L; X ss = elapsed_seconds; X X sprintf(elapsed_time_str,"%02ld:%02ld:%02ld",hh,mm,ss); X return(elapsed_time_str); X} /* end of get_elapsed_time */ X X/*+----------------------------------------------------------------------- X char *get_tod(type,tod) X X time of day types: X 0 hh:mm X 1 hh:mm:ss X 2 mm-dd-yyyy hh:mm X X static string address is returned X if tod != (char *)0, time is returned there too X------------------------------------------------------------------------*/ Xchar * Xget_tod(type,tod) Xint type; Xchar *tod; X{ X long cur_time = 0; X struct tm *lt; /* local time */ X static char tod_str[32]; X struct timeb tp; X X cur_time = time((long *)0); X lt = localtime(&cur_time); X#if defined(M_XENIX) && defined(MESSED_UP_DST) X ftime(&tp); /* localtime() does not know about DST */ X if(tp.dstflag) /* so we adjust with ftime, which does know */ X { X cur_time += 3600L; /* problem in BSD too, but no ftime there */ X lt = localtime(&cur_time); X cur_time -= 3600L; X } X#endif X X switch(type) X { X case 0: X sprintf(tod_str,"%02d:%02d",lt->tm_hour,lt->tm_min); X break; X X default: X case 1: X sprintf(tod_str,"%02d:%02d:%02d",lt->tm_hour,lt->tm_min,lt->tm_sec); X break; X X case 2: X sprintf(tod_str,"%02d-%02d-%04d %02d:%02d", X lt->tm_mon + 1,lt->tm_mday,lt->tm_year + 1900, X lt->tm_hour,lt->tm_min); X break; X } X X if(tod != (char *)0) X strcpy(tod,tod_str); X X return(tod_str); X} /* end of get_tod */ X X/*+------------------------------------------------------------------------- X clear_area(win,row,col,len) X--------------------------------------------------------------------------*/ Xclear_area(win,row,col,len) XWINDOW *win; Xint row; Xint col; Xint len; X{ X wmove(win,row,col); X while(len-- > 0) X waddch(win,' '); X wmove(win,row,col); X X} /* end of clear_area */ X X/*+------------------------------------------------------------------------- X clear_area_char(win,row,col,len,fillchar) X--------------------------------------------------------------------------*/ Xclear_area_char(win,row,col,len,fillchar) XWINDOW *win; Xint row; Xint col; Xint len; Xchar fillchar; X{ X wmove(win,row,col); X while(len-- > 0) X waddch(win,fillchar); X wmove(win,row,col); X X} /* end of clear_area_char */ X X/*+------------------------------------------------------------------------- X report_top_line(topstr) X top line: row 1 col 18 length 39 X--------------------------------------------------------------------------*/ Xvoid Xreport_top_line(topstr) Xchar *topstr; X{ Xchar s42[42]; X clear_area(win,1,18,39); X if(strlen(topstr) < 39) X waddstr(win,topstr); X else X { X strncpy(s42,topstr,39); X s42[39] = 0; X waddstr(win,s42); X } X} /* end of report_top_line */ X X/*+------------------------------------------------------------------------- X report_protocol_crc_type(str) X X protocol crc type: row 1 col 10 length 6 X--------------------------------------------------------------------------*/ Xreport_protocol_crc_type(str) Xregister char *str; X{ Xchar s8[8]; X X if(strlen(str) > 6) X { X strncpy(s8,str,6); X s8[6] = 0; X str = s8; X } X clear_area(win,1,10,6); X waddstr(win,str); X wrefresh(win); X if(log_packets) X { X write(log_packets,"chk: ",6); X write(log_packets,str,strlen(str)); X write(log_packets,"\n",1); X } X X} /* end of report_protocol_crc_type */ X X/*+------------------------------------------------------------------------- X report_uninit() X--------------------------------------------------------------------------*/ Xvoid Xreport_uninit() X{ Xfloat total = (float)total_data_chars_xfered; X X if(curses_installed) X { X if((total_data_chars_xfered != 0L) && (elapsed_seconds != 0L)) X { X sprintf(s128,"Data xfer rate ~= %6.0f chars/sec", X total / (float)elapsed_seconds); X if(log_packets) X { X write(log_packets,"info: ",6); X write(log_packets,s128,strlen(s128)); X write(log_packets,"\n",1); X } X report_top_line(s128); X } X report_file_byte_io(0L); X report_rx_tx_count(); X wmove(win,WIN_LINES - 1,WIN_COLS - 1); X wrefresh(win); X endwin(); X fprintf(stderr,"\r\n\r\n\r\n"); X fflush(stderr); X curses_installed = 0; X } X X} /* end of report_uninit */ X X/*+------------------------------------------------------------------------- X report_init(title) X--------------------------------------------------------------------------*/ Xvoid Xreport_init(title) Xchar *title; X{ Xregister int itmp; Xregister char *cptr; Xint monitor_type; Xchar buf[48]; X X if(curses_installed) X return; X X initscr(); X crmode(); X noecho(); X nonl(); X clear(); X curses_installed = 1; X win = newwin(WIN_LINES,WIN_COLS,WIN_TOPY,WIN_LEFTX); X box(win,sVR,sHR); X wmove(win,0,0); waddch(win,sTL); X wmove(win,win->_maxy - 1,0); waddch(win,sBL); X wmove(win,win->_maxy - 1,win->_maxx - 1); waddch(win,sBR); X wmove(win,0,win->_maxx - 1); waddch(win,sTR); X wmove(win,0,2); X wstandout(win); X waddch(win,'['); X waddch(win,' '); X waddstr(win,title); X waddch(win,' '); X waddch(win,']'); X wstandend(win); X waddch(win,sHR); X waddch(win,sHR); X waddstr(win," dir: "); X waddstr(win,curr_dir); X waddch(win,' '); X X itmp = 0; X while(1) X { X if(win_template[itmp] == (char *)0) X break; X wmove(win,itmp + 1,1); X waddstr(win,win_template[itmp++]); X } X if(sending_flag) X { X clear_area(win,2,15,3); X sprintf(s128,"%3d",npaths); X waddstr(win,s128); X#if defined(FORK_DEBUG) X sprintf(s128,"ecusz pid %d",getpid()); X#endif X } X else /* ecurz */ X { X clear_area(win,2,11,8); /* clear "of ###" */ X waddstr(win,":"); X#if defined(FORK_DEBUG) X sprintf(s128,"ecurz pid %d",getpid()); X#endif X } X X#if defined(FORK_DEBUG) X ecu_log_event(getppid(),s128); X#endif X X clear_area(win,1,11,47); X report_error_count(); X clear_area(win,8,26,8); /* starting time */ X waddstr(win,get_tod(1,(char *)0)); X start_seconds = time((long *)0); X current_seconds = start_seconds; X X if(ioctl(0,CONS_GET,&monitor_type) < 0) /* not multiscreen */ X { X sTL = vanilla_TL; X sTR = vanilla_TR; X sBL = vanilla_BL; X sBR = vanilla_BR; X sLT = vanilla_LT; X sRT = vanilla_RT; X sVR = vanilla_VR; X sHR = vanilla_HR; X } X X wrefresh(win); X X} /* end of report_init */ X X/*+------------------------------------------------------------------------- X report_rx_ind(status) X--------------------------------------------------------------------------*/ Xvoid Xreport_rx_ind(status) Xint status; X{ X wmove(win,1,54); X waddch(win,(status) ? 'R' : ' '); X wmove(win,1,54); X wrefresh(win); X} /* end of report_rx_ind */ X X/*+------------------------------------------------------------------------- X report_tx_ind(status) X--------------------------------------------------------------------------*/ Xvoid Xreport_tx_ind(status) Xint status; X{ X wmove(win,1,56); X waddch(win,(status) ? 'T' : ' '); X wmove(win,1,56); X wrefresh(win); X} /* end of report_tx_ind */ X X/*+------------------------------------------------------------------------- X report_rx_tx_count() X X rx char count: row 6 col 16 length 8 unsigned long X tx char count: row 6 col 29 length 8 unsigned long X now time of day: row 1 col 50 length 8 hh:mm:ss X This procedure may be counted upon to perform wrefresh(win) X Xelapsed time row 9 col 26 length 8 Xcurrent tod row 9 col 47 length 8 X--------------------------------------------------------------------------*/ Xreport_rx_tx_count() X{ X extern unsigned long rx_char_count; X extern unsigned long tx_char_count; X X register char *cptr; X X sprintf(s128,"%8ld",rx_char_count); X wmove(win,6,16); X waddstr(win,s128); X sprintf(s128,"%8ld",tx_char_count); X wmove(win,6,29); X waddstr(win,s128); X X /* now time of day */ X clear_area(win,9,47,8); X cptr = get_tod(1,(char *)0); X waddstr(win,cptr); X current_seconds = time((long *)0); X elapsed_seconds = current_seconds - start_seconds; X cptr = get_elapsed_time(elapsed_seconds); X clear_area(win,9,26,8); X waddstr(win,cptr); X wrefresh(win); /* calling procs expect this to occur always */ X X} /* end of report_rx_tx_count */ X X/*+------------------------------------------------------------------------- X report_line(baud_rate,mode) X--------------------------------------------------------------------------*/ Xvoid Xreport_line(baud_rate,mode) Xunsigned baud_rate; Xchar *mode; X{ Xchar s16[16]; X wmove(win,7,14); X sprintf(s16,"%5u",baud_rate); X waddstr(win,s16); X clear_area(win,7,52,6); X waddstr(win,mode); X wrefresh(win); X} /* end of report_line */ X X/*+------------------------------------------------------------------------- X report_rxpos(rxpos) row 3 col 19 len 8 X--------------------------------------------------------------------------*/ Xvoid Xreport_rxpos(rxpos) Xlong rxpos; X{ Xchar tmp[16]; Xchar refr; X X if(rdchk(0)) X { X read(0,&refr,1); X if(refr == 0x0C || refr == 0x012) /* ^L or ^R */ X { X write(2,"\033[2J",4); X nap((long)60); X touchwin(stdscr); X wrefresh(stdscr); X touchwin(win); X wrefresh(win); X } X } X X if((rxpos > 99999999L) || (rxpos < 0L)) X return; X X sprintf(tmp,"%8lu",rxpos); X wmove(win,3,19); X waddstr(win,tmp); X wrefresh(win); X report_rx_tx_count(); /* which will do a refresh */ X} /* end of report_rxpos */ X X/*+------------------------------------------------------------------------- X report_txpos(txpos) X--------------------------------------------------------------------------*/ Xvoid Xreport_txpos(txpos) Xlong txpos; X{ X report_rxpos(txpos); X} /* end of report_txpos */ X X/*+------------------------------------------------------------------------- X report_error_count() X DOES NOT PERFORM A REFRESH CYCLE X this file: row 10 col 29 len 4 X total: row 10 col 42 len 6 X--------------------------------------------------------------------------*/ Xreport_error_count() X{ X char tmp[16]; X X clear_area(win,10,29,4); X sprintf(tmp,"%4d",this_file_errors); X waddstr(win,tmp); X X clear_area(win,10,42,6); X sprintf(tmp,"%6d",total_errors); X waddstr(win,tmp); X wrefresh(win); X X} /* end of report_error_count */ X X/*+------------------------------------------------------------------------- X report_last_txhdr(rptstr,error_flag) X 5,7,22 X--------------------------------------------------------------------------*/ Xvoid Xreport_last_txhdr(rptstr,error_flag) Xregister char *rptstr; Xint error_flag; X{ Xchar s24[24]; X X if(log_packets) X { X write(log_packets,"tx: ",6); X write(log_packets,rptstr,strlen(rptstr)); X write(log_packets,"\n",1); X } X X if(strlen(rptstr) > 22) X { X strncpy(s24,rptstr,22); X s24[23] = 0; X rptstr = s24; X } X clear_area(win,5,7,22); X waddstr(win,rptstr); X wrefresh(win); X X if(error_flag) X { X ++this_file_errors; X ++total_errors; X report_error_count(); X } X} /* end of report_last_txhdr */ X X/*+------------------------------------------------------------------------- X report_last_rxhdr(rptstr,error_flag) X 5,35,22 X--------------------------------------------------------------------------*/ Xvoid Xreport_last_rxhdr(rptstr,error_flag) Xregister char *rptstr; Xint error_flag; X{ Xchar s24[24]; Xextern int log_packets; X X if(log_packets) X { X write(log_packets,"rx: ",4); X write(log_packets,(error_flag) ? "E " : " ",2); X write(log_packets,rptstr,strlen(rptstr)); X write(log_packets,"\n",1); X } X X if(strlen(rptstr) > 22) X { X strncpy(s24,rptstr,22); X s24[23] = 0; X rptstr = s24; X } X clear_area(win,5,35,22); X waddstr(win,rptstr); X wrefresh(win); X X if(error_flag) X { X ++this_file_errors; X ++total_errors; X report_error_count(); X } X X} /* end of report_last_rxhdr */ X X/*+------------------------------------------------------------------------- X report_str(rptstr,error_flag) row 11/12 col 3 len 55 X X error_flag == 0 for status/progress message X == 1 for bump error count, unless rptstr is null X in which case, merely clear error string area X == 2 write string on bottom line (not an error) X == 3 write string on transaction line (not an error) X == -1 use error line but do not bump error count X--------------------------------------------------------------------------*/ Xvoid Xreport_str(rptstr,error_flag) Xregister char *rptstr; Xint error_flag; X{ Xchar s60[60]; Xextern int log_packets; X X if(strlen(rptstr) > 55) X { X strncpy(s60,rptstr,55); X s60[56] = 0; X rptstr = s60; X } X X switch(error_flag) X { X case 0: X clear_area(win,12,3,55); X break; X case 1: X ++this_file_errors; X ++total_errors; X report_error_count(); X case -1: X clear_area(win,11,3,55); X break; X case 2: X clear_area(win,13,3,55); X break; X case 3: X clear_area(win,4,3,55); X break; X } X X waddstr(win,rptstr); X wrefresh(win); X X if(log_packets) X { X write(log_packets,"info: ",6); X sprintf(s60,"%2d ",error_flag); X write(log_packets,s60,3); X write(log_packets,rptstr,strlen(rptstr)); X write(log_packets,"\n",1); X } X X} /* end of report_str */ X X/*+------------------------------------------------------------------------- X report_transaction() X--------------------------------------------------------------------------*/ Xvoid Xreport_transaction(str) Xchar *str; X{ X report_str(str,3); X} /* end of report_transaction */ X X/*+------------------------------------------------------------------------- X report_file_open_tod() -- time of start of this file X X this file open time: row 8 col 47 length 8 X--------------------------------------------------------------------------*/ Xvoid Xreport_file_open_tod() X{ X clear_area(win,8,47,8); X waddstr(win,get_tod(1,(char *)0)); X wrefresh(win); X} /* end of report_file_open_tod */ X X/*+------------------------------------------------------------------------- X report_file_open_length(long_length) X length: row 3 col 36 len 8 X--------------------------------------------------------------------------*/ Xreport_file_open_length(length) Xlong length; X{ X clear_area(win,3,36,8); X if(length <= 0) X waddstr(win,"unknown"); X else X { X sprintf(s128,"%8lu",length); X waddstr(win,s128); X } X wrefresh(win); X} /* end of report_file_open_length */ X X/*+------------------------------------------------------------------------- X report_file_send_open(filename,filestat) X X filename: row 2 col 20 len 38 X number: row 2 col 8 len 3 X length: row 3 col 36 len 8 X mode: row 3 col 46 len 10 X time of start of this file: row 4 col 47 length 8 hh:mm:ss X--------------------------------------------------------------------------*/ Xvoid Xreport_file_send_open(filename,filestat) Xchar *filename; Xstruct stat *filestat; X{ Xchar s50[50]; Xregister char *cptr = filename; Xchar s256[256]; X X if(log_packets) X { X write(log_packets,"file: ",6); X write(log_packets,filename,strlen(filename)); X write(log_packets,"\n",1); X } X X /* number */ X clear_area(win,2,8,3); X sprintf(s50,"%3d",Filcnt); X waddstr(win,s50); X X /* filename */ X if(strlen(filename) > 38) X { X strncpy(s50,filename,38); X s50[39] = 0; X cptr = s50; X } X clear_area(win,2,20,38); X waddstr(win,cptr); X X#if defined(LOG_XFER) X sprintf(s256,"sending %s",filename); X ecu_log_event(getppid(),s256); X#endif X X /* length */ X report_file_open_length(filestat->st_size); X X /* time of start of this file */ X report_file_open_tod(); X X this_file_errors = 0; X report_error_count(); X} /* end of report_file_send_open */ X X/*+------------------------------------------------------------------------- X report_file_rcv_started(filename,length,last_mod_time) X X filename: row 2 col 7 len 50 X length: row 3 col 36 len 8 if not xmodem X time of start of this file: row 4 col 47 length 8 hh:mm:ss X--------------------------------------------------------------------------*/ Xreport_file_rcv_started(filename,length,last_mod_time) Xchar *filename; Xlong length; /* if < 0, "UNKNOWN" */ Xlong last_mod_time; /* not currently displayed */ X{ Xregister char *cptr; Xchar s50[50]; Xchar s256[256]; X X if(log_packets) X { X write(log_packets,"file: ",6); X write(log_packets,filename,strlen(filename)); X write(log_packets,"\n",1); X } X X /* filename */ X if(strlen(filename) > 38) X { X strncpy(s50,filename,38); X s50[39] = 0; X cptr = s50; X } X else X cptr = filename; X X#if defined(LOG_XFER) X sprintf(s256,"receiving %s",filename); X ecu_log_event(getppid(),s256); X#endif X X clear_area(win,2,20,38); X waddstr(win,cptr); X X /* file number */ X clear_area(win,2,8,3); X sprintf(s50,"%3d",Filcnt); /* rz uses as file number 1-n */ X waddstr(win,s50); X X/* if remote sender provides a file count, display it */ X if(npaths) X { X clear_area(win,2,12,7); /* clear "of ###" */ X sprintf(s50,"of %3d:",npaths); X waddstr(win,s50); X } X X /* length */ X report_file_open_length(length); X X /* time of start of this file */ X report_file_open_tod(); X X this_file_errors = 0; X report_error_count(); X} /* end of report_file_rcv_started */ X X/*+------------------------------------------------------------------------- X report_file_close() X--------------------------------------------------------------------------*/ Xvoid report_file_close() X{ X report_str("End of file",0); X wrefresh(win); X X} /* end of report_file_close */ X X/*+------------------------------------------------------------------------- X report_file_byte_io(count) X--------------------------------------------------------------------------*/ Xreport_file_byte_io(count) Xlong count; X{ X X total_data_chars_xfered += (long)count; X if(total_data_chars_xfered) X { X sprintf(s128,"Total file bytes transferred: %lu", X total_data_chars_xfered); X report_str(s128,-1); X } X X} /* end of report_file_byte_io */ X X/* end of scurses.c */ X/* vi: set tabstop=4 shiftwidth=4: */ SHAR_EOF $TOUCH -am 1208015790 'sea/scurses.c' && chmod 0644 sea/scurses.c || echo 'restore of sea/scurses.c failed' Wc_c="`wc -c < 'sea/scurses.c'`" test 22139 -eq "$Wc_c" || echo 'sea/scurses.c: original size 22139, current size' "$Wc_c" # ============= sea/sealink.doc ============== echo 'x - extracting sea/sealink.doc (Text)' sed 's/^X//' << 'SHAR_EOF' > 'sea/sealink.doc' && X SEALINK X X File Transfer Protocol X X 9 February 1987 X X X X (C) COPYRIGHT 1986,87 by System Enhancement Associates, Inc. X X X X This document describes briefly the SEAlink file transfer protocol X developers' package. SEAlink is a sliding window protocol that is X fully backwards compatible with XMODEM in all tested implementations. X X The intent of SEAlink is to provide a file transfer protocol that does X not suffer from propagation delays, such as are introduced by X satellite relays or packet switched networks. Actual tests of the X enclosed routines has shown that SEAlink is capable of virtually X eliminating propagation delays and turnaround delays. File transfers X between New Jersey and Hawaii, which normally suffer a degradation of X 50% or more due to satellite relays, proceed as fast as local X transfers. Even transfers within the local exchange are speeded up by X up to 20% at 2400 baud by the elimination of turnaround delays. Large X volume tests show that SEAlink is capable of coming to within 2% of X the theoretical minimum time for data transfer. X X X X The developers' package contains the following files: X X SEALINK.DOC This document. X SEALINK.C A set of C routines for implementing SEAlink. X CLINK.EXE A sample TTY program that implements SEAlink. X X X X You are granted a license to use this code in your programs, and to X adapt it to your particular situation and needs, subject only to the X following conditions: X X 1) You must refer to it as the SEAlink protocol, and you must give X credit to System Enhancement Associates. X X 2) If you modify it in such a way that your version cannot converse X with the original code as supplied by us, then you should refer to X it as "SEAlink derived", or as a "variation of SEAlink", or words X to that effect. X X In short, we're not asking for any money, but we'd like to get some X credit for our work. X X X This document is not meant to be a rigorous definition of the X protocol. The code provided should serve to document the details and X fine points of implementing SEAlink. We will, however, present a X brief synopsis of how SEAlink adds sliding windows to XMODEM, and why X XMODEM doesn't mind. X X First of all, SEAlink adds a block number to the ACK and NAK used in X XMODEM.(1) We thus create "ACK/NAK packets", with the following X structure: X X Byte 0: ACK, NAK, or C X Byte 1: Block number X Byte 2: One's compliment of block number X X This is identical in form to the first three bytes of a data packet, X except that the SOH has been replaced with an ACK or NAK.(2) X X From the receiver's point of view, it does not matter if the trans- X mitter is using sliding window or not. The receiver simply sends ACK X and NAK packets as appropriate. Any XMODEM driver tested to date will X simply ignore this excess data behind the ACK or NAK. X X From the transmitter's point of view, it just barely matters if the X receiver can handle sliding window. The transmitter always acts as if X it is sending sliding window, but varies the window size. If it is X seeing valid block numbers and check values behind the received ACKs X and NAKs, it sets the window size to six blocks. Otherwise, it sets X the window size to one block. The result is that it only "sends X ahead" if the receiver can handle it. X X It should be a fairly simple matter to apply the underlying SEAlink X logic to almost any variant of XMODEM. X X X The SEAlink routines provided in this package are also capable of X passing system dependent information, such as true file size and time X of last modification. This data is passed in a special header block. X The header block looks exactly like any other block, except that it is X block number zero. X X This is still backwards compatible with XMODEM, as a SEAlink receiver X does not mind if block zero is missing, and any XMODEM receiver yet X tested will regard block zero as a duplicate block and ACK it. X X The data portion of block zero contains the following fields: X X X Offset Size Contents X ====== ==== ======== X X 0 4 Original file length. X 4 4 Date and time file was last mofified, in X seconds since 1979. X 8 17 Original file name, as a null terminated X string. X 25 15 Name of transmitting program, as a null X terminated string. X 40 88 Null filler and expansion area. X X X (1) XMODEM/CRC uses a "C" in place of a NAK to indicate CRC error X detection. SEAlink follows this convention, and supports either X checksum or CRC. For brevity, this document will use the term NAK X to mean either a true NAK (hex 15) or a C (hex 43). X (2) See previous footnote. X X Any field which the transmitter cannot support should be set to all X zeros. Conversly, the receiver should ignore any null fields. The X receiver may ignore any field which he cannot support. X X X X The routines enclosed in this package should be reasonably easy to X implement in your application. We have attempted to exclude compiler X dependent and system dependent logic from these routines. X X X You will need to alter our references to our communications driver to X conform to your own driver. The communications related routines we X use are: X X com_putc(c) Output character c to comm port. X X int com_getc(t) Get character from comm port within t tenths X of a second. Return EOF if time limit X expires. X X com_dump() Discard any pending output without sending it. X X X In addition, we use the following routines for controlling timed X loops: X X long timerset(t) Set a timer. Returns a timer value which will X expire in t tenths of a second. X X int timeup(z) Check a timer. Returns true if timer z has X expired yet, or false otherwise. X X X These routines also make reference to the following functions for X system dependent information, which is optional: X X filestat(name,&fs) Read directory entry for a file and return X system dependent information. X X setstamp(f,dtg) Set a file's date/time of last modification. X X X X X The SEAlink implementation provided in this package is used by X invoking the two primary routines: X X X int xmtfile(name) /* transmit a file */ X char *name; /* name of file to transmit */ X X This routine is used to send a file. One file is sent at a time. If X the name is blank (name is null or *name points to a null), then an X end of transmission marker is sent. X X This routine returns a one if the file is successfully transmitted, or X a zero if a fatal error occurs. X X X char *rcvfile(name) /* receive a file */ X char *name; /* name of file (optional) */ X X This routine is used to receive a file. One file is received. If a X name is specified for the file, then that name WILL be used, and any X name sent by the transmitter will be ignored. If the name is blank X (name is null or *name points to a null), then the transmitter must X provide a name for the file. X X This routine returns a pointer to the name of the file that was X received. If the file transfer is not successful, then a null pointer X is returned. X X The pointer returned by rcvfile() points to a static data buffer. X This does not have to be freed (and should not be), but it will be X overwritten the next time rcvfile() is called. X X The rcvfile() function works on a temporary file whose name is the X same as the final file, but with a dash ("-") added at the beginning. X If a file transfer is aborted, then this temporary file will be X retained. An aborted file transfer will not harm a pre-existing file X of the same name. X X X X These routines can be used for either single or multiple file X transfers. X X To send multiple files, send each file one by one until either a X transmit fails or all files are sent. If all files are sent, then X signal the end by calling xmtfile() with a null pointer. X X To receive multiple files, call rcvfile() repeatedly until it returns X a null pointer. X X X X This package includes a demonstration program named CLINK (pronounced X "clink"), which is a simple TTY program for doing SEAlink file X transfers. CLINK does not perform any sort of terminal emulation X whatsoever. However, she will make use of the ANSI.SYS screen driver X if you have it installed. X X X CLINK may be used in either of two ways: interactive mode or command X mode. X X To use CLINK in the interactive mode, give the command "CLINK" with no X arguments. Press the "ESCape" key to give a command to CLINK. The X command "?" (question mark) instructs CLINK to tell you what commands X she understands. X X To use CLINK in the command mode, give the command "CLINK" with an X argument. There are three arguments you can give CLINK in the command X mode. These are: X X 1) Receive files; Do this with a command of the form: X X CLINK R X X CLINK will attempt to receive one or more files from COM1, and X will terminate as soon as all files are received, or when the X transfer aborts. X X 2) Transmit files; Do this with a command of the form: X X CLINK T <filename> ... X X CLINK will attempt to transmit the listed files over COM1, and X will terminate as soon as all files are sent, or the transfer is X aborted. <filename> may be one or more file names with or without X drive and path specifiers. Wildcards may be used. X X 3) Give help; If you type: X X CLINK ? X X or any invalid command, CLINK will display a brief reminder of X what arguments she understands in command mode. X X In all cases, CLINK in the command mode will not alter the serial port X other than to set eight data bits, one stop bit, and no parity. Any X previously installed serial drivers will be replaced, and the baud X rate will not be changed. X X X X CLINK comes with her own serial driver built in for the IBM PC family X and true compatibles, but she is capable of using any standard FOSSIL X driver. X SHAR_EOF $TOUCH -am 1114021689 'sea/sealink.doc' && chmod 0644 sea/sealink.doc || echo 'restore of sea/sealink.doc failed' Wc_c="`wc -c < 'sea/sealink.doc'`" test 11247 -eq "$Wc_c" || echo 'sea/sealink.doc: original size 11247, current size' "$Wc_c" # ============= sea/sealink.imp ============== echo 'x - extracting sea/sealink.imp (Text)' sed 's/^X//' << 'SHAR_EOF' > 'sea/sealink.imp' && X SEAlink - Sliding window file transfer protocol X X XENIX System V version by Scott Reynolds X additional XENIX modifications by Sanford Zelkovitz, without whose help X this couldn't have been accomplished X X Based on: X X MS-DOS Version 1.16, created on 01/15/87 at 01:40:52 X(C) COPYRIGHT 1986,87 by System Enhancement Associates; ALL RIGHTS RESERVED X By: Thom Henderson X X Description: X X This file contains a set of routines to illustrate the SEAlink X sliding window file transfer protocol. SEAlink is fully backward X compatible to XMODEM, and can be easily adapted to most XMODEM X variants. X X The intent of SEAlink is to provide a file transfer protocol that X does not suffer from propagation delays, such as are introduced X by satellite relays or packet switched networks. X X Instructions: X X Two routines are provided to implement SEAlink file transfers. X X int xmtfile(name) /+ transmit a file +/ X char *name; /+ name of file to transmit +/ X X This routine is used to send a file. One file is sent at a time. X If the name is blank (name is null or *name points to a null), X then only an end of transmission marker is sent. X X This routine returns a one if the file is successfully X transmitted, or a zero if a fatal error occurs. X X char *rcvfile(name) /+ receive a file +/ X char *name; /+ name of file (optional) +/ X X This routine is used to receive a file. One file is received. X The name, if given, takes precedence and will be the name of X the resulting file. If the name is blank (name is null or *name X points to a null), then the name given by the transmitter is used. X If the transmitter does not give a name, then the file transfer X is aborted. X X This routine returns a pointer to the name of the file that X was received. If the file transfer is not successful, then X a null pointer is returned. X X The pointer returned by rcvfile() points to a static data buffer. X This does not have to be freed (and should not be), but it will X be overwritten the next time rcvfile() is called. X X The rcvfile() function works on a temporary file whose name is X the same as the final file, but with a period (".") added at the X beginning. If a file transfer is aborted, then this temporary X file will be retained. An aborted file transfer will not harm X a pre-existing file of the same name. X X Programming notes: X X These routines can be used for either single or multiple file X transfers. X X To send multiple files, send each one one at a time until either X a transmit fails or all files are sent. If all files are sent, X then signal the end by calling xmtfile() with a null pointer. X X To receive multiple files, call rcvfile() repeatedly until it X returns a null pointer. X X These routines pass a "block zero", which contains information X about the original file name, size, and date/time of last X modification. If you cannot implement block zero, then you can X leave it out. If you cannot set any given field in block zero X when transmitting, then you should leave it set to zeros. If you X cannot use any given field of block zero when receiving, then X you should ignore it. X X These routines are fully compatible with XMODEM, including the X original checksum method and later CRC adaptations. It can be X easily adapted to Modem7 protocol by adding a Modem7 filename X transfer shell, though we do not recommend it. The underlying X logic, of course, can be adapted to almost any variant of XMODEM. X X License: X X You are granted a license to use this code in your programs, and X to adapt it to your particular situation and needs, subject only X to the following conditions: X X 1) You must refer to it as the SEAlink protocol, and you must X give credit to System Enhancement Associates. X X 2) If you modify it in such a way that your version cannot X converse with the original code as supplied by us, then X you should refer to it as "SEAlink derived", or as a X "variation of SEAlink", or words to that effect. X X In short, we're not asking for any money, but we'd like to X get some credit for our work. X X Language: X X Computer Innovations C86 X Adapted for IBM PC XENIX 2.00.2 C using UNIX System V compatible calls X X Notes on XENIX modifications: X X The com_getc() routine has a minimum delay of .1 seconds, due X to the nature of the read() system call. Attempts to eliminate X this delay have proven more costly than leaving it in. X X CRC maintenance functions were added to the original code, as X they are not library calls under XENIX. X X All output is performed through file descriptor 0, and is done X in blocks using the write() system call rather than individual X character writes. File descriptor 1 may be selected by invoking X the program with a "-1" argument. X X Most low level routines utilize register class variables to X decrease overhead and improve overall system response slightly. X X A rudimentary command line processor was added to the original X routines to drive the transmitter and receiver. The two X options, "s" and "r", are for sending and receiving respectively. X X When invoked without proper arguments the program will display X a short message including usage notes. X X -- Scott Reynolds, Sysop U.S.S. Enterprise BBS, (906)228-9460 SHAR_EOF $TOUCH -am 0917230787 'sea/sealink.imp' && chmod 0644 sea/sealink.imp || echo 'restore of sea/sealink.imp failed' Wc_c="`wc -c < 'sea/sealink.imp'`" test 5213 -eq "$Wc_c" || echo 'sea/sealink.imp: original size 5213, current size' "$Wc_c" # ============= gendial/Makefile ============== if test ! -d 'gendial'; then echo 'x - creating directory gendial' mkdir 'gendial' fi echo 'x - extracting gendial/Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'gendial/Makefile' && X# CHK=0xE274 X# X# uucp dialer makefile X# X#+:EDITS:*/ X#:11-29-1990-18:09-wht@n4hgf-add dialgMPAD X#:07-19-1990-17:12-root@n4hgf-add gendial/dialgT2500 X XLDFLAGS = -s -i XCFLAGS = -Octl -DHDUU X XEXES = dialgT2500 dialgT1000 XOBJS = XLIBS = -lx X X.SUFFIXES: X.SUFFIXES: .c .o .h X X.c.o:; cc -c $(CFLAGS) $*.c X Xall: dialgT2500 X#all: $(EXES) X XdialgMC9624: gendial.o dceMC9624.o X cc $(CFLAGS) gendial.o dceMC9624.o $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialgTBPlus: gendial.o dceTBPlus.o X cc $(CFLAGS) gendial.o dceTBPlus.o $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialgMPAD: gendial.o dceMPAD.o X cc $(CFLAGS) gendial.o dceMPAD.o $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialgT2500: gendial.o dceT2500.o X cc $(CFLAGS) gendial.o dceT2500.o $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialgT1000: gendial.o dceT1000.o X cc $(CFLAGS) gendial.o dceT1000.o $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X X# provide for standard SCO dialers XdialHA12: dialHA12.c X cc $(CFLAGS) dialHA12.c $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialHA24: dialHA24.c X cc $(CFLAGS) dialHA24.c $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialMUL: dialMUL.c X cc $(CFLAGS) dialMUL.c $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialTBIT: dialTBIT.c X cc $(CFLAGS) dialTBIT.c $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialT2500: dialT2500.c X cc $(CFLAGS) dialT2500.c $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X XdialVA3450: dialVA3450.c X cc $(CFLAGS) dialVA3450.c $(LIBS) -o $@ X chown uucp $@; chgrp uucp $@; chmod 711 $@ X Xgendial.o: dialer.h XdceT2500.o: dialer.h XdceMC9624.o: dialer.h SHAR_EOF $TOUCH -am 1129180990 'gendial/Makefile' && chmod 0644 gendial/Makefile || echo 'restore of gendial/Makefile failed' Wc_c="`wc -c < 'gendial/Makefile'`" test 1713 -eq "$Wc_c" || echo 'gendial/Makefile: original size 1713, current size' "$Wc_c" # ============= gendial/dialer.h ============== echo 'x - extracting gendial/dialer.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'gendial/dialer.h' && X/*+------------------------------------------------------------------------- X dialer.h - SCO UUCP generic dialer program definitions X wht%n4hgf.uucp@emory.mathcs.emory.edu -or- emory!n4hgf!wht X--------------------------------------------------------------------------*/ X/*+:EDITS:*/ X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */ X X#include <stdio.h> X#include <ctype.h> X#include <errno.h> X#include <fcntl.h> X#include <setjmp.h> X#include <signal.h> X#include <sys/types.h> X#include <sys/errno.h> X#include <sys/ioctl.h> X#include <sys/stat.h> X#include <termio.h> X#include <time.h> X#include <pwd.h> X X#define ff fprintf X#define se stderr X Xchar *strdup(); Xchar *strchr(); Xchar *strrchr(); Xstruct passwd *getpwnam(); X Xextern int errno; Xextern char *sys_errlist[]; X Xextern int gargc; /* global copy of main's argv */ Xextern char **gargv; /* global copy of main's argv */ Xextern char *dce_name; /* full pathname of ACU device */ Xextern char *telno; /* phone number if dial type request */ Xextern struct termio dce_termio; /* last termio for device */ Xextern int Debug; /* set per -x flag */ Xextern int dialing; /* set while dialing in progress */ Xextern int fddce; /* file descriptor for dce_name */ Xextern int DialerExitCode; /* return code */ Xextern int status; /* set on errors */ Xextern int hangup_flag; /* set when DCE being hung up */ Xextern int hiCBAUD; /* highest permissible baud rate */ Xextern int loCBAUD; /* lowest permissible baud rate */ Xextern int uid; /* user id of executor */ Xextern int uid_uucp; /* user id of uucp */ Xextern int secure; /* non-zero to suppress display of secure X * DCE traffic X */ X Xunsigned char dialer_codes[26]; /* A-Z embedded phone number codes */ X/* return codes: these are set up so that an abort signal at any time can */ X/* set the fail bit and return to the caller with the correct status */ X#define SUCCESS 0 X#define RC_FAIL 0x80 /* 1 = failed to connect */ X#define RC_ENABLED 0x10 /* enabled flag: 1 = ungetty -r required to X * restore the line X */ X#define RC_BAUD 0x0f /* CBAUD connected at (0=same as dialed speed) */ X X/* DCE result device independent flag */ X#define rfNumeric 0x40000000 X X/* program exit codes */ X#define RCE_NULL 0 /* general purpose or unknown error code */ X#define RCE_INUSE 1 /* line in use */ X#define RCE_SIG 2 /* signal aborted dialer */ X#define RCE_ARGS 3 /* invalid arguments */ X#define RCE_PHNO 4 /* invalid phone number */ X#define RCE_SPEED 5 /* invalid baud rate -or- bad connect baud */ X#define RCE_OPEN 6 /* can't open line */ X#define RCE_IOCTL 7 /* ioctl error */ X#define RCE_TIMOUT 8 /* timeout */ X#define RCE_NOTONE 9 /* no dial tone */ X#define RCE_HANGUP 10 /* hangup failed */ X#define RCE_NORESP 11 /* Modem didn't respond. */ X#define RCE_BUSY 13 /* phone is busy */ X#define RCE_NOCARR 14 /* no carrier */ X#define RCE_ANSWER 15 /* no answer */ X X/* ungetty return codes */ X#define UG_NOTENAB 0 X#define UG_ENAB 1 X#define UG_RESTART 1 X#define UG_FAIL 2 X X/* size for various buffers */ X#define MAXLINE 80 X X/* How many errors allowed before call retry fails */ X#define DIAL_ERRORS_MAX 4 X X/* DCE message to code mapping struct ... array DCE_results of these X * must be terminated with { (char *)0,0 } */ Xtypedef struct dce_result X{ X char *result; X long code; X} DCE_RESULT; X X#define DEBUG(level,fmt,arg) if (Debug >= level) fprintf(stderr,fmt,arg) X#if !defined(DBG) X#define DBG 0 X#endif X X/* X * what the hell does __STDC__ mean in reality? An __STDC__ compiler is X * more nouveau than an older one. ANSI C (or 'D') just stirred new X * food for "standard" readers who went off and did what they wanted X * to do. We use __STDC__ to decide between two opinions of X * what constitute "ANSI prototypes." As of this writing, __STDC__ is X * defined by the UNIX (MSC 5) compiler and not by the XENIX (MSC 4) X * compiler. X */ X#ifdef __STDC__ Xextern int DCE_baud_to_CBAUD(unsigned int baud); Xextern void DCE_hangup(void ); Xextern int DCE_dial(char *telno); Xextern void DCE_abort(int sig); Xextern void DCE_exit(int exitcode); Xextern int DCE_argv_hook(int argc,char * *argv,int optind,int unrecognized_switches); Xextern int get_uucp_uid(void ); Xextern int instr(char *s1,char *s2); Xextern void translate(char *ttab,char *str); Xextern int decode_phone_number(char *userphno,char *result,int resultlen); Xextern char *make_printable(unsigned char ch); Xextern char *RCE_text(int value); Xextern void myexit(int code); Xextern int dial_abort(int sig); Xextern void cleanup(int stat); Xextern int SIGALRM_abort(int sig); Xextern int SIGALRM_alert(int sig); Xextern long _lread(int rtime,int error_ok); Xextern long lread_ignore(int rtime); Xextern long lread(int rtime); Xextern int lflush(void ); Xextern void _lputc(char lchar); Xextern void _lputc_paced(long pace_msec,char lchar); Xextern void _lputs(char *string); Xextern void _lputs_paced(long pace_msec,char *string); Xextern int lwrite(char *str); Xextern int ltoggleDTR(long msec); Xextern int call_ungetty(char call_type); Xextern void display_termio(struct termio *ttt,char *text); Xextern int open_dce(void ); Xextern int main(int argc,char * *argv); X#else Xextern int DCE_baud_to_CBAUD(unsigned int baud); Xextern void DCE_hangup(void ); Xextern int DCE_dial(char *telno); Xextern void DCE_abort(int sig); Xextern void DCE_exit(int exitcode); Xextern int DCE_argv_hook(int argc,char * *argv,int optind,int unrecognized_switches); Xextern int get_uucp_uid(void ); Xextern int instr(char *s1,char *s2); Xextern void translate(char *ttab,char *str); Xextern int decode_phone_number(char *userphno,char *result,int resultlen); Xextern char *make_printable(unsigned char ch); Xextern char *RCE_text(int value); Xextern void myexit(int code); Xextern int dial_abort(int sig); Xextern void cleanup(int stat); Xextern int SIGALRM_abort(int sig); Xextern int SIGALRM_alert(int sig); Xextern long _lread(int rtime,int error_ok); Xextern long lread_ignore(int rtime); Xextern long lread(int rtime); Xextern int lflush(void ); Xextern void _lputc(char lchar); Xextern void _lputc_paced(long pace_msec,char lchar); Xextern void _lputs(char *string); Xextern void _lputs_paced(long pace_msec,char *string); Xextern int lwrite(char *str); Xextern int ltoggleDTR(long msec); Xextern int call_ungetty(char call_type); Xextern void display_termio(struct termio *ttt,char *text); Xextern int open_dce(void ); Xextern int main(int argc,char * *argv); X#endif X X/* vi: set tabstop=4 shiftwidth=4: */ X/* end of dialer.h */ SHAR_EOF $TOUCH -am 0919224990 'gendial/dialer.h' && chmod 0644 gendial/dialer.h || echo 'restore of gendial/dialer.h failed' Wc_c="`wc -c < 'gendial/dialer.h'`" test 6539 -eq "$Wc_c" || echo 'gendial/dialer.h: original size 6539, current size' "$Wc_c" true || echo 'restore of gendial/gendial.c failed' echo End of part 24, continue with part 25 exit 0 -------------------------------------------------------------------- Warren Tucker, TuckerWare emory!n4hgf!wht or wht@n4hgf.Mt-Park.GA.US Hacker Extraordinaire d' async PADs, pods, proteins and protocols exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.