[comp.unix.microport] Clock.shar / revised nbs_time

sandy@turnkey.TCC.COM (Sanford 'Sandy' Zelkovitz) (07/31/88)

Since I sent through the net, nbs_time, a NEW and MUCH improved version of
the program has been uploaded to my bbs by the author, Warren H. Tucker.
Not only did he improve the main program but he also included a few handy
additional programs as enhancements. Below, you will find all of the source 
code, in SHAR format, as sent to me.
 
Sanford ( Sandy ) Zelkovitz
XBBS  714-898-8634

------------------------------ Cut / Snip / Ax  Here ---------------------------


#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./CMOSstime.c`
then
echo "writing ./CMOSstime.c"
cat > ./CMOSstime.c << '\Rogue\Monster\'
/* CHK=0xC608 */
/*+-------------------------------------------------------------------------
	CMOSstime.c -- read cmos clock then stime()

  Defined functions:
	date_to_epochsecs(year,month,day,hour,min,sec)
	days_since_epoch(year,month,day)
	leap_year(yr)
	main(argc,argv,envp)

  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA 30075
  (404)587-5766

--------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-23-1988-18:59-wht-release */
/*:07-22-1988-19:34-wht-creation */

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <time.h>

#include "dev_cmos.h"

int leap_year(int );
int days_since_epoch(int ,int ,int );
long date_to_epochsecs(int ,int ,int ,int ,int ,int );

char	*ctime(long *);
int		stime(long *);
long	time(long *);

char	*cmos = "/dev/cmos";

/*+-------------------------------------------------------------------------
	leap_year(yr) -- returns 1 if leap year, 0 otherwise 
--------------------------------------------------------------------------*/
int
leap_year(yr)
int yr;
{
	return (yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0);
}	/* end of leap_year */

/*+-------------------------------------------------------------------------
	days_since_epoch(year,month,day)
Return the number of days between Jan 1, 1970 and year,month,day
year is full year (e.g., 1988)
month is 0-11
day is 0-30 (must be legal day in month)
--------------------------------------------------------------------------*/
int
days_since_epoch(year,month,day)
int		year;
int		month;
int		day;
{
register int days = day + 1;	/* adjust for day 0-n instead of 1-n */
register int m;
register int y;
register char *md = "\37\34\37\36\37\36\37\37\36\37\36\37";

	for (y = 1970; y < year; ++y) 
	{
		days += 365;
		if (leap_year(y)) ++days;
	}
	for (m = 0; m < month; ++m)
		days += md[m] + (m == 1 && leap_year(y));

	return(days);

}	/* end of days_since_epoch */

/*+-------------------------------------------------------------------------
	date_to_epochsecs(year,month,day,hour,min,sec)
Convert arguments into long seconds since 1/1/1970
year is full year (e.g., 1988)
month is 0-11
day is 0-30 (must be legal day in month)
hour is 0-23, min and sec 0-59
--------------------------------------------------------------------------*/
long
date_to_epochsecs(year,month,day,hour,min,sec)
int		year;
int		month;
int		day;
int		hour;
int		min;
int		sec;
{
register int m1;
register int m2;
long	t;
struct tm *tp;
struct tm *localtime();

	t = (days_since_epoch(year,month,day) - 1) * 86400L
		+ (hour * 3600L) + (min * 60L) + sec;

	/* Now the hard part -- correct for the time zone */
	tp = localtime(&t);
	m1 = tp->tm_hour * 60 + tp->tm_min;
	m2 = (hour * 60) + min;
	t -= ((m1 - m2 + 720 + 1440) % 1440 - 720) * 60L;
	return(t);

}	/* end of date_to_epochsecs */

/*+-------------------------------------------------------------------------
	main(argc,argv,envp)
--------------------------------------------------------------------------*/
main(argc,argv,envp)
int		argc;
char	**argv;
char	**envp;
{
int		year;
int		month;
int		day;
int		hour;
int		min;
int		sec;
int		fdcmos;
long	now;

	setbuf(stdout,NULL);
	setbuf(stderr,NULL);

	if((fdcmos = open(cmos,O_RDONLY)) < 0)
	{
		perror(cmos);
		exit(1);
	}

	get_clock(fdcmos,&year,&month,&day,&hour,&min,&sec);
	now = date_to_epochsecs(year,month,day,hour,min,sec);
	if(stime(&now))
	{
		perror("stime");
		exit(1);
	}
	time(&now);
	fputs("System time after stime(): ",stdout);
	fputs(ctime(&now), stdout);

	exit(0);
}	/* end of main */
\Rogue\Monster\
else
  echo "will not over write ./CMOSstime.c"
fi
if `test ! -s ./NBSsetclk.c`
then
echo "writing ./NBSsetclk.c"
cat > ./NBSsetclk.c << '\Rogue\Monster\'
/* CHK=0x0BFA */
/*+-----------------------------------------------------------------------
  SCO XENIX SYSTEM V.2  (Others too?)

  NBSsetclk.c -- call NBS, get time, hangup quickly, set system time,
                 update cmos clock (/dev/cmos)

  Defined functions:
	create_lock_file(lock_file_name)
	hangup(sig)
	hayes_dial()
	hayes_send_cmd(cmd)
	lclose()
	lgetc(char_rtnd)
	lgetc_timeout(timeout_msec)
	lgets_timeout(lrwt)
	lkill_buf()
	lock_tty()
	lopen()
	lputc(lchar)
	lputs_paced(pace_msec,string)
	lrdchk()
	lset_baud_rate(ioctl_flag)
	lset_parity(ioctl_flag)
	main(argc,argv,envp)
	make_lock_name(ttyname,lock_file_name)
	other_lock_name(first_lock_name)
	to_lower(ch)
	to_upper(ch)
	ulcmpb(str1,str2)
	ulindex(str1,str2)
	unlock_tty()
	usage()
	valid_baud_rate(baud)


  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA  30075
  (404)587-5766

  Note: must be root to execute

  Exit status codes for the program:
  1-n  signal values if program killed
  200  illogical cmos device behavior (when reading clock)
  249  stime() call failed
  250  could not open communications line
  251  /dev/cmos read&write access denied
  252  user is not root
  253  usage
  254  couldn't get time from line (after 40 retries! BAD line conditions)
  255  could not establish a connection

------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-23-1988-19:03-wht-release */
/*:07-23-1988-13:11-wht-get good parsed time before hanging up w/max retries */
/*:07-23-1988-12:50-wht-do not use setclock ... messes up new system time */
/*:07-22-1988-20:17-wht-change output format */
/*:07-18-1988-22:07-wht-working! */
/*:07-18-1988-17:27-wht-creation */

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termio.h>

#include "dev_cmos.h"

#ifndef ushort
#define ushort	unsigned short
#endif
#ifndef uchar
#define uchar	unsigned char
#endif
#ifndef uint
#define uint	unsigned int
#endif
#ifndef ulong
#define ulong	unsigned long
#endif

void usage(void);
char to_upper(char );
char to_lower(char );
int ulcmpb(unsigned char  *,unsigned char  *);
int ulindex(char  *,char  *);
void hangup(int );
int make_lock_name(char  *,char  *);
int create_lock_file(char  *);
char  *other_lock_name(char  *);
int lock_tty(void );
void unlock_tty(void );
int valid_baud_rate(unsigned int );
int lset_baud_rate(int );
void lset_parity(int );
void lgetc(char  *);
int lrdchk(void );
void lputc(char );
void lputs_paced(int ,char  *);
char  *lgets_timeout(struct lrwt  *);
int lgetc_timeout(unsigned long );
void lkill_buf(void );
int lopen(void );
void lclose(void );
int hayes_send_cmd(char  *);
int hayes_dial(void );

ushort	geteuid();
ushort	getuid();
long	nap(long);
long 	time(long *);
char	*ctime(long *);

typedef struct lrwt	/* param to lgets_timeout in eculine.c */
{
ulong	to1;		/* timeout for 1st character (granularity 20) */
ulong	to2;		/* timeout for each next char (granularity 20) */
int		raw_flag;	/* !=0, rtn full buffer, ==0, rtn filtered hayes result */
char	*buffer;	/* buffer to fill */
int		bufsize;	/* size of buffer */
int		count;		/* from proc, count rcvd */
}	LRWT;

#define	EPOCH		40587					/* UNIX starts JD 2440587, */
#define	leap(y, m)	((y+m-1 - 70%m) / m)	/* also known as 1/1/70 */
#define	TONE		'*'
/* #define	TIME		"\n%05ld %03d %02d%02d%02d UTC" */
#define	TIME		"%05ld %03d %02d%02d%02d UTC"

/* for better source line utilization, frequent use of 'fprintf' and 'stderr'
   warrants the following */
#define pf	printf
#define	ff	fprintf
#define se	stderr
#define so	stdout

/* lopen() and related routines error codes */
#define LOPEN_INVALID	-1		/* for invalid tty name */
#define LOPEN_UNKPID	-2		/* unknown pid using line */
#define LOPEN_LCKERR	-3		/* lock file open error */
#define LOPEN_NODEV		-4		/* device does not exist */
#define LOPEN_OPNFAIL	-5		/* count not open line */
#define LOPEN_ALREADY	-6		/* line already open */

extern char	*revision;		/* ecurev.c temp file from buildrev */
extern char	*numeric_revision;	/*ecunumrev.c */

char	LLCKname[128];		/* lock file name */
char	Ltelno[64];			/* telephone number for remote or null */
char	Lline[64];			/* line name */
int		Liofd;				/* file descriptor for line */
int		Lparity;			/* 0==NONE, 'e' == even, 'o' == odd */
struct termio	Llv;		/* attributes for the line to remote */
uint	Lbaud;				/* baud rate */
ushort	euid;
ushort	uid;
char	*cmos = "/dev/cmos";

/*+-------------------------------------------------------------------------
	usage()
--------------------------------------------------------------------------*/
void
usage()
{
	ff(se,"Usage: nbs_time [-][-e][-o][-n][-b#][-t#]\n");
	ff(se,"Defaults 1200-N %s %s\n",Ltelno,Lline);
	ff(se," -        use defaults\n");
	ff(se," -e       even parity\n");
	ff(se," -o       odd parity\n");
	ff(se," -n       no parity\n");
	ff(se," -b#      baud rate\n");
	ff(se," -t#      telephone number\n");
	ff(se," -l<name> line (/dev/tty??)\n");
	exit(253);

}	/* end of usage */

/*+-------------------------------------------------------------------------
    to_upper() / to_lower()
  one would think that these were relatively standard
  types of thing, but MSC/Xenix specifies toupper() to convert to upper
  case if not already and Unix says to adjust without testing,
  so, two stupid little routines here
  ASCII only -- no EBCDIC gradoo here please
--------------------------------------------------------------------------*/
char to_upper(ch)
register char    ch;
{ return( ((ch >= 'a') && (ch <= 'z')) ? ch - 0x20 : ch);
}   /* end of to_upper() */

char to_lower(ch)
register char    ch;
{ return( ((ch >= 'A') && (ch <= 'Z')) ? ch + 0x20 : ch);
}   /* end of to_lower() */

/*+----------------------------------------------------------------------------
    ulcmpb(str1,str) -- Upper/Lower [case insensitive] Compare Bytes

 Returns -1 if strings are equal, else failing character position
 If the second strings terminates with a null and both strings have matched
 character for character until that point, then -1 is returned.
 NOTE:  this is not a test for complete equality of two strings, but allows
 discovery of a string as a substring in a larger containing string.
-----------------------------------------------------------------------------*/
int
ulcmpb(str1,str2)
register unsigned char    *str1;
register unsigned char    *str2;
{
register int     istr;

    for( istr=0 ; ;  ++istr )
    {
        if(str2[istr] == '\0')          /* if second string exhausts, match! */
            return(-1);
        if((str1[istr] == '\0' ) ||
			( to_upper(str1[istr]) != to_upper(str2[istr]) ))
            return(istr);
    }
	/*NOTREACHED*/
} /* end of ulcmpb */

/*+-------------------------------------------------------------------------
    ulindex:  Upper/Lower [case insensitive] Index functioni

  Returns position of 'str2' in 'str1' if found
  If 'str2' is null, then 0 is returned (null matches anything)
  Returns -1 if not found

  uses 'ulcmpb'
--------------------------------------------------------------------------*/
int ulindex(str1,str2)
register char	*str1;	/* the (target) string to search */
register char	*str2;	/* the (comparand) string to search for */
{
register int	istr1 = 0;		/* moving index into str1 */
register char	*mstr = str1;	/* moving string pointer */

    if(str2[0] == '\0')             /* null string matches anything */
        return(0);
	while(1)
    {
        if(*mstr == '\0')           /* if we exhaust target string, flunk */
            return(-1);
        /* Can we find either case of first comparand char in target? */
        if( to_upper(*mstr) == to_upper(str2[0]) )
        {
            /* we have a first char match... does rest of string match? */
            if(ulcmpb(mstr,str2) == -1)         /* if the rest matches, ... */
                return(istr1);                  /* ... return match position */
        }
        /* we did not match this time... increment istr1, mstr and try again */
        ++istr1;
        ++mstr;
    }
}	/* end of ulindex */

/*+-----------------------------------------------------------------------
	hangup(sig) -- terminate program (with comm line cleanup)
------------------------------------------------------------------------*/
void
hangup(sig)
int		sig;
{
void	lclose();

	ff(se,"\n");
	if(Liofd != -1)
		lclose();			/* close line */
	exit(sig);
}	/* end of hangup */

/*+-------------------------------------------------------------------------
	make_lock_name(ttyname,lock_file_name)
--------------------------------------------------------------------------*/
make_lock_name(ttyname,lock_file_name)
char	*ttyname;
char	*lock_file_name;
{
register int itmp;
register char *ttyptr;

	if((itmp = ulindex(ttyname,"/dev/tty")) != 0)
		return(LOPEN_INVALID);

	itmp = ulindex(ttyname,"tty");

	ttyptr = &ttyname[itmp];
	strcpy(lock_file_name,"/usr/spool/uucp/LCK..");
	strcat(lock_file_name,ttyptr);
	return(0);

}	/* end of make_lock_name */

/*+-----------------------------------------------------------------------
	create_lock_file()

  Returns 0 if lock file created,else error codes:
	LOPEN_ if error
    else pid of process currently busy on device
------------------------------------------------------------------------*/
create_lock_file(lock_file_name)
char	*lock_file_name;
{
register int	fd_lockf;
int		pid;
int		old_umask;
int		erc = 0;

	old_umask = umask(0);

	if((fd_lockf = open(lock_file_name,O_CREAT | O_EXCL | O_RDWR,0666)) < 0)
	{		/* file already exists */
		if((fd_lockf = open(lock_file_name,O_RDWR,0666)) < 0)
		{
			erc = LOPEN_LCKERR;
			goto RESTORE_UMASK;
		}
		else if(read(fd_lockf,(char *)&pid,sizeof(pid))) 
		{
			if(kill(pid,0)) 				/* is owner pid already dead? */
			{
				if(errno == ESRCH)			/* this error sez so */
				{
					pid = getpid();			/* so we will use it */
					lseek(fd_lockf,0L,0);
					write(fd_lockf,(char *)&pid,sizeof(pid));
					close(fd_lockf);
					erc = 0;
					goto RESTORE_UMASK;
				}
			} 
			/* owner pid still active with lock */
			close(fd_lockf);
			erc = pid;			/* port is busy */
			goto RESTORE_UMASK;
		}
		else
		{
			close(fd_lockf);
			erc = LOPEN_UNKPID;
			goto RESTORE_UMASK;
		}
	} 
	pid = getpid();
	write(fd_lockf,(char *)&pid,sizeof(pid));

	close(fd_lockf);
	chmod(lock_file_name,0666);

RESTORE_UMASK:
	(void)umask(old_umask);
	return(erc);

}	/* end of create_lock_file */

/*+-------------------------------------------------------------------------
	other_lock_name(first_lock_name)
--------------------------------------------------------------------------*/
char *
other_lock_name(first_lock_name)
char	*first_lock_name;
{
register int itmp;
static char other_lock_name[64];

	strcpy(other_lock_name,first_lock_name);
	itmp = strlen(other_lock_name) - 1;
	if(islower(other_lock_name[itmp]))
		other_lock_name[itmp] = toupper(other_lock_name[itmp]);
	else if(isupper(other_lock_name[itmp]))
		other_lock_name[itmp] = tolower(other_lock_name[itmp]);

	return(other_lock_name);
		
}	/* end of other_lock_name */

/*+-------------------------------------------------------------------------
	lock_tty()
--------------------------------------------------------------------------*/
lock_tty()
{
register int itmp;
struct stat ttystat;

	if(itmp = make_lock_name(Lline,LLCKname))
		return(itmp);

	if(stat(Lline,&ttystat) < 0)
		return(LOPEN_NODEV);

	if(itmp = create_lock_file(LLCKname))
		return(itmp);

	if(itmp = create_lock_file(other_lock_name(LLCKname)))
	{
		unlink(LLCKname);
		LLCKname[0] = 0;
		return(itmp);
	}

}	/* end of lock_tty */

/*+-----------------------------------------------------------------------
	void unlock_tty()
------------------------------------------------------------------------*/
void
unlock_tty()
{

	if(LLCKname[0] == 0)
		return;

	unlink(LLCKname);
	unlink(other_lock_name(LLCKname));

	LLCKname[0] = 0;
}	/* end of unlock_tty */

/*+-------------------------------------------------------------------------
	valid_baud_rate(baud) -- returns (positive) baud rate selector
or -1 if invalid baud rate
--------------------------------------------------------------------------*/
valid_baud_rate(baud)
uint	baud;
{
	switch(baud)
	{
		case 110: return(B110);
		case 300: return(B300);
		case 600: return(B600);
		case 1200: return(B1200);
		case 2400: return(B2400);
		case 4800: return(B4800);
		case 9600: return(B9600);
		case 19200: return(EXTA);
		case 38400: return(EXTB);
		default: return(-1);
	}

}	/* end of valid_baud_rate */

/*+-----------------------------------------------------------------------
	lset_baud_rate(ioctl_flag)

  If 'ioctl_flag' is set,then ioctl(Liofd,TCSETA,&Llv)
  is executed after setting baud rate
------------------------------------------------------------------------*/
lset_baud_rate(ioctl_flag)
int		ioctl_flag;
{
int		baud_selector = valid_baud_rate(Lbaud);

	if(baud_selector < 0)
	{
		ff(se,"invalid baud rate: %u\n",Lbaud);
		ff(se,"valid rates: 110,300,600,1200,2400,4800,9600,19200\n");
		return(1);
	}
	Llv.c_cflag &= ~CBAUD;
	Llv.c_cflag |= baud_selector;

	if(ioctl_flag)
		 ioctl(Liofd,(int)TCSETA,(char *)&Llv);
	return(1);

}	/* end of lset_baud_rate */

/*+-----------------------------------------------------------------------
	lset_parity(ioctl_flag)

  If 'ioctl_flag' is set,then ioctl(Liofd,TCSETA,&Llv)
  is executed after setting parity
------------------------------------------------------------------------*/
void
lset_parity(ioctl_flag)
int		ioctl_flag;
{
	Llv.c_cflag &= ~(CS8 | PARENB | PARODD);
	switch(to_lower(Lparity))
	{
		case 'e':
			Llv.c_cflag |= CS7 | PARENB;
			Llv.c_iflag |= ISTRIP;
			break;
		case 'o':
			Llv.c_cflag |= PARODD | CS7 | PARENB;
			Llv.c_iflag |= ISTRIP;
			break;
		default:
			ff(se,"invalid parity: %c ... defaulting to no parity\n");
		case 0:
		case 'n':
			Llv.c_cflag |= CS8;
			Llv.c_iflag &= ~(ISTRIP);
			Lparity = 0;
			break;
	}			

	if(ioctl_flag)
		 ioctl(Liofd,(int)TCSETA,(char *)&Llv);

}	/* end of lset_parity */

/*+-------------------------------------------------------------------------
	lgetc(char_rtnd)
--------------------------------------------------------------------------*/
void
lgetc(char_rtnd)
char	*char_rtnd;
{

READ_AGAIN:
	errno = 0;
	if(read(Liofd,char_rtnd,1) < 1)
	{
		if(errno == EINTR)			/* if signal interrupted, ... */
			goto READ_AGAIN;
		hangup(254);
	}

}	/* end of lgetc */

/*+-------------------------------------------------------------------------
	lrdchk() -- rdchk(Liofd)
--------------------------------------------------------------------------*/
int
lrdchk()
{
	return(rdchk(Liofd));

}	/* end of lrdchk */

/*+-----------------------------------------------------------------------
	lputc(lchar) -- write lchar to comm line
------------------------------------------------------------------------*/
void
lputc(lchar)
char	lchar;
{
	while(write(Liofd,&lchar,1) != 1)
	{
		if(errno == EINTR)
			continue;
		hangup(255);
	}
}	/* end of lputc */

/*+-----------------------------------------------------------------------
	lputs_paced(pace_msec,string) -- write string to comm line
  with time between each character 
------------------------------------------------------------------------*/
void
lputs_paced(pace_msec,string)
register int pace_msec;
register char	*string;
{
register long msec = (pace_msec) ? (long)pace_msec : (long)20;

	while(*string)
	{
		lputc(*string++);
		nap(msec);
	}

}	/* end of lputs_paced */

/*+-------------------------------------------------------------------------
	char *lgets_timeout(LRWT *)

typedef struct lrwt
{
ulong	to1;
ulong	to2;
int		raw_flag;
char	*buffer;
int		bufsize;
int		count;
}	LRWT;

to1 and to2 are unsigned long values in milliseconds (not
currently supported well under BSD4); to1 is the time to wait
for the first character, to2 the time to wait for subsequent
characters.

if raw_flag 0,     non-printables are stripped from beginning
                   and end of received characters (i.e., modem
                   response reads); NULs discarded, parity stripped
if raw_flag 1,     full raw read buffer returned
if raw_flag 2,     full buffer, NULs discarded, parity stripped

buffer is address to read chars into

bufsize is buffer max size (allowing room for terminating null)
which should be at least 2 if raw_size includes 0x80 bit,
else at least 12 characters if 0x80 omitted.

count is a int which, at return, receives the actual count read

--------------------------------------------------------------------------*/
char *
lgets_timeout(lrwt)
LRWT	*lrwt;
{
register int actual_count = 0;
register char	*cptr = lrwt->buffer;
int		max_count = lrwt->bufsize;
char	*rtn_val;
int		timeout_counter;
int		qc1;
int		qc2;
long	quantum;
long	ltmp;

/* minimum wait is 60 msec */
	if(Lbaud < 300)
		if(lrwt->to2 < 300L) lrwt->to2 = 300L;
	if(Lbaud < 1200)
		if(lrwt->to2 < 200L) lrwt->to2 = 200L;
	else
		if(lrwt->to2 < 60L) lrwt->to2 = 60L;

/* shortest interval */
	ltmp = (lrwt->to1 < lrwt->to2) ? lrwt->to1 : lrwt->to2;

/* calculate wait quantum */
	quantum = ltmp / 10L;				/* try for ten ticks */
	if(quantum < 20L)
		quantum = 20L;
	qc1 = lrwt->to1 / quantum;
	if(!qc1) qc1 = 1L;
	qc2 = lrwt->to2 / quantum;
	if(!qc2) qc2 = 1L;

/* perform the lrtw function
   input: qc1 is first nap count (for first charcters) 
          qc2 is 2nd nap count (for subsequent characters) 
          quantum is the nap period in milliseconds
          cptr is char* to receive read string
          max_count is max number of characters incl null
          lrwt->raw_flag as described above

  output: lrwt->count is actual count of return result
          lrwt->buffer is return read buffer
*/
	max_count--;				/* leave room for null */

	lrwt->raw_flag &= 0x0F;		/* get rid of 0xF0 flags */
	timeout_counter = qc1;		/* first timeout */ 
	*cptr = 0;					/* init result string */
	while(timeout_counter--)
	{
		nap(quantum);
		while(lrdchk())
		{
			lgetc(cptr);
			if(lrwt->raw_flag != 1)
			{
				*cptr &= 0x7F;
				if(*cptr == 0)
					continue;
			}

			*++cptr = 0;
			actual_count++;
			if(--max_count == 0)
				goto READ_LINE_POST_PROCESS;
			timeout_counter = qc2;
		}
	}

READ_LINE_POST_PROCESS:
	if(lrwt->raw_flag)
	{
		lrwt->count = actual_count;
		return(lrwt->buffer);
	}
	cptr = lrwt->buffer;
	while(((*cptr >0) && (*cptr < 0x20)) || (*cptr >= 0x7F))
		cptr++;
	rtn_val = cptr;
	actual_count = 0;
	while(((*cptr &= 0x7F) >= 0x20) && (*cptr <= 0x7E))
	{
		cptr++;
		actual_count++;
	}
	*cptr = 0;
	strcpy(lrwt->buffer,rtn_val);
	lrwt->count = actual_count;
	return(lrwt->buffer);
}	/* end of lgets_timeout */

/*+-------------------------------------------------------------------------
	lgetc_timeout(timeout_msec)

 reads one character from line unless timeout_msec passes with no receipt.
 timeout_msec < 20 msec becomes 20 msec
 return char (raw - parity bit preserved) if received, else -1 if timeout
--------------------------------------------------------------------------*/
int
lgetc_timeout(timeout_msec)
ulong	timeout_msec;
{
LRWT	lr;
char	getc_buf[2];		/* room for one char + null */

	lr.to1 = timeout_msec;
	lr.to2 = timeout_msec;
	lr.raw_flag = 1;		/* full raw read */
	lr.buffer = getc_buf;
	lr.bufsize = sizeof(getc_buf);
	lgets_timeout(&lr);
	return(  (lr.count == 1) ? (int)getc_buf[0] : -1 );

}	/* end of lgetc_timeout */

/*+-------------------------------------------------------------------------
	lkill_buf()
--------------------------------------------------------------------------*/
void
lkill_buf()
{
	ioctl(Liofd,(int)TCFLSH,(char *)2); /* flush input and output */
}	/* end of lkill_buf */

/*+----------------------------------------------------------------------
	lopen()
returns negative LOPEN_ codes if failure else positive pid using line
else 0 if successful open
------------------------------------------------------------------------*/
int
lopen()
{
register int	itmp;

	if(Liofd >= 0)
		return(LOPEN_ALREADY);
	if(itmp = lock_tty())		/* get lock file */
		return(itmp);
	Liofd = open(Lline,O_RDWR,0777);
	if(Liofd < 0)
		return(LOPEN_OPNFAIL);
	else
	{
		ioctl(Liofd,(int)TCGETA,(char *)&Llv);
		Llv.c_iflag = (IGNPAR | IGNBRK | IXOFF );
		Llv.c_cflag |= (CREAD | HUPCL);
		Llv.c_lflag = 0;

		Llv.c_cc[VMIN]   = 1;
		Llv.c_cc[VTIME]  = 1;

		lset_baud_rate(0);		/* do not perform ioctl */
		lset_parity(1);			/* do perform ioctl */
	}

	return(0);

}	/* end of lopen */

/*+-----------------------------------------------------------------------
	lclose()
------------------------------------------------------------------------*/
void
lclose()
{
	if(Liofd < 0)
		return;
	ioctl(Liofd,(int)TCGETA,(char *)&Llv); /* save initial state */
	Llv.c_cflag |= HUPCL;
	ioctl(Liofd,(int)TCSETA,(char *)&Llv);
	close(Liofd);
	Liofd = -1;
	unlock_tty();		/* kill lock file */

}	/* end of lclose */

/*+-------------------------------------------------------------------------
	hayes_send_cmd(cmd)
  0: success (cmd accepted)
 -1: cannot talk to modem
--------------------------------------------------------------------------*/
hayes_send_cmd(cmd)
char	*cmd;
{
register char	*cptr;
int retry = 0;

	cptr = cmd;
	lkill_buf();
	while(1)
	{
		lputc(0x07);	/* something random */
		if(lgetc_timeout(500L) < 0)
		{
			if(retry)
				return(-1);
			retry = 1;
			lputs_paced(0,"ATQ0E1V1\r");
			nap((long)1500);
			lkill_buf();
			continue;
		}
		break;
	}
	while(*cptr)
	{
		lputc(*cptr++);
		if(lgetc_timeout(500L) < 0)
			return(-1);
	}
	lputc('\r');
	if(lgetc_timeout(500L) < 0)
		return(-1);
	return(0);

}	/* end of hayes_send_cmd */

/*+-----------------------------------------------------------------------
	hayes_dial()
returns 1 on success (CONNECT), 
		0 if failure to connect
		-1 if cannot talk to modem
------------------------------------------------------------------------*/
int
hayes_dial()
{
register int itmp;
char	s128[128];
int		rtn_code = -1;	/* assume fail, CONNECT will chg to zero */
int		s7;
LRWT	lr;

	s7 = 30;
	strcpy(s128,"ATV1E1S11=45DT" );
	strcat(s128,Ltelno);

	if(itmp = hayes_send_cmd(s128))
		return(itmp);

/* some modems (ahem, the Hayes 2400) do not accurately honor S7 */
	lr.to1 = s7 * 3 * 1000L;
	lr.to2 = 100L;
	lr.raw_flag = 0;
	lr.buffer = s128;
	lr.bufsize = sizeof(s128);
	ff(se,"Dialing %s ... INT to abort ... ",Ltelno);
	fflush(se);
	lgets_timeout(&lr);
	if(lr.count)
		ff(se,"%s\n",s128);
	if(strncmp(s128,"CONNECT",7) == 0)
		return(1);
	return(0);

}	/* end of hayes_dial */

/*+-------------------------------------------------------------------------
	main(argc,argv,envp)

  main() program forks to create rcvr process; then main()
  becomes the xmtr process
------------------------------------------------------------------------*/
main(argc,argv,envp)
int		argc;
char	**argv;
char	**envp;
{
char	*cptr;
int		iargv;
int		swchar;
int		itmp;
LRWT	lr;
char	rd_buf[64];
long	now;
long	julian;
long	time_at_connect;
int		day_of_year;
int		year;
int		month;
int		day;
int		hour;
int		min;
int		sec;
int		fdcmos;
int		have_time = 0;
int		max_tries;
struct tm *lt;

	setbuf(stderr,NULL);
	setbuf(stdout,NULL);

/* init line variables */
	strcpy(Lline,"/dev/tty1a");
	strcpy(Ltelno,"1(202)653-0351");
	Liofd = -1;
	Lbaud = 1200;
	Lparity = 0;

	if(argc < 2)
		usage();

	if((argc == 2) && (!strcmp(argv[1],"-")))
		;
	else
	{
		for(iargv = 1; iargv < argc; iargv++)
		{
			if(*argv[iargv] != '-')
				continue;
			switch(*(argv[iargv] + 1))
			{
				case 'e': Lparity = 'e'; break;
				case 'o': Lparity = 'o'; break;
				case 'n': Lparity =  0 ; break;
				case 'b': Lbaud = atoi(argv[iargv] + 2); break;
				case 't': strcpy(Ltelno,argv[iargv] + 2); break;
				case 'l': strcpy(Lline,argv[iargv] + 2); break;
				default:  usage();
			}
		}
	}

	uid = getuid();
	euid = geteuid();
	if((euid == 0) || (uid == 0))	/* if root running or prog text ... */
		nice(-40);
	else
	{
		ff(se,"must be root\n");
		exit(252);
	}

	if((fdcmos = open(cmos,O_RDWR,0)) < 0)
	{
		perror(cmos);
		exit(251);
	}

	signal(SIGHUP,hangup);
	signal(SIGQUIT,hangup);
	signal(SIGINT,hangup);
	signal(SIGTERM,hangup);

	if(itmp = lopen())
	{
		switch(itmp)
		{
			case LOPEN_INVALID:
				ff(se,"invalid line name\n"); break;
			case LOPEN_UNKPID:
				ff(se,"unknown pid is using line\n"); break;
			case LOPEN_LCKERR:
				ff(se,"lock file error\n"); break;
			case LOPEN_NODEV:
				ff(se,"line does not exist\n"); break;
			case LOPEN_ALREADY:
				ff(se,"line already open\n"); break;
			case LOPEN_OPNFAIL:
				ff(se,"line open error\n"); break;
			default:
				ff(se,"pid %d using line\n",itmp); break;
		}
		exit(250);
	}

	lr.to1 = 1100L;
	lr.to2 =  100L;
	lr.raw_flag = 0;	/* full raw read */
	lr.buffer = rd_buf;
	lr.bufsize = sizeof(rd_buf);

/* spend money on long distance call ... only time freaks can understand */
	if(!hayes_dial())
		hangup(255);
	time_at_connect = time((long *)0);

	max_tries = 40;		/* 40 sec max connect time (charged for 1 min anyway) */
	while(!have_time)
	{
		for(itmp = 0; itmp < 30; itmp++)		/* look for asterisk */
		{
			if(lgetc_timeout(500L) == TONE)
				break;
		}

		lgets_timeout(&lr);		/* get date/time string */

		if(sscanf(lr.buffer,TIME,&julian,&day_of_year,&hour,&min,&sec) != 5)
		{
			ff(se,"garbled result: '%s'  ",lr.buffer);
			if(--max_tries)
			{
				puts("... retrying");
				continue;
			}
			puts("... aborting");
			break;		/* too many retries ... BAAAAD line condx */
		}
		have_time = 1;
	}

	lclose();

/* no longer spending money */

	fprintf(stdout,"Connect time %ld second(s)\n",
		time((long *)0) - time_at_connect);

	if(have_time)
	{
		now = (((julian - EPOCH) * 24 + hour) * 60 + min) * 60 + sec;
		if(stime(&now) < 0)
		{
			perror("stime");
			exit(249);
		}
		fputs("Time retrieved from standard: ",stdout);
		fputs(ctime(&now), stdout);
		time(&now);
		lt = localtime(&now);
		fputs("setting cmos clock ... ",stdout);
		set_clock(fdcmos,lt->tm_year,lt->tm_mon,lt->tm_mday,
				lt->tm_hour,lt->tm_min,lt->tm_sec);
		fputs("result: ",stdout);
		get_clock(fdcmos,&year,&month,&day,&hour,&min,&sec);
		printf("%02d/%02d/%04d %02d:%02d:%02d\n",month,day,year,hour,min,sec);
		exit(0);
	}
	else
	{
		puts("Did not get time ... sorry");
		exit(254);
	}

}	/* end of main */

/* end of nbs_time.c */

\Rogue\Monster\
else
  echo "will not over write ./NBSsetclk.c"
fi
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\Rogue\Monster\'
Abstract
--------------------------------------------------------------------
XENIX V supports the 286 cmos ram with two devices (/dev/cmos,
/dev/clock), programs /etc/setclock and /lib/cvtdate and a shell
script /etc/asktime.

However, there is no way to set the system time or the cmos clock
ACCURATELY using these tools since seconds are not part of the
paradigm of setclock or /dev/clock.  One can, however, set the cmos
clock accurately by the direct manipulation of /dev/cmos.  Then again,
by reading /dev/cmos, one can get the correct time and set the
system time with stime(S).

Overview of cmostime.tar.Z
--------------------------------------------------------------------
This collection of programs allow you to:

1) set the cmos clock and system time very accurately by calling
   the Naval Observatory ((202)653-0351 in DC) using 'NBSsetclk'

2) set the system clock from the (somwhat drifty) cmos clock
   by using 'CMOSstime'

3) display interesting things in the cmos ram by using 'cmos_disp'

The file is compressed to 12-bits (using compress16 thanks to
Sandy Zelkovitz's BBS at 1(714)898-8634).  When tar is run on
the uncompressed file, a group of files consisting of the sources,
executables and Makefile is produced.

List of files:
CMOSstime CMOSstime.c Makefile NBSsetclk NBSsetclk.c README
at_cmos.h cmos_disp cmos_disp.c dev_cmos.c dev_cmos.h NBSsetclk

--------------------------------------------------------------------
'NBSsetclk' MUST be run by root.  Why waste a long distance call
only to have stime() fail?  Most user will use the default parameters
of this program by invoking it with 'NBSsetclk -'.  Running the
program with no parameters gives you the options available.
NBSsetclk assumes a Hayes modem is connected to the line and
that command recognition is enabled.  /dev/tty1a is used by default.
The program tries up to 40 times (40 seconds) to read the time,
making it likely that you will succeed.  However, the program will
terminate before the minimum 1 minute connect charge has expired.

Exit status codes for the NBSsetclk:
1-n  signal values if program killed
200  illogical cmos device behavior (when reading clock)
249  stime() call failed
250  could not open communications line
251  /dev/cmos read&write access denied
252  user is not root
253  usage
254  couldn't get time from line (after 40 retries! BAD line conditions)
255  couldn't establish a connection

CMOSstime
--------------------------------------------------------------------
'CMOSstime' must be run by root.  Run it in your /etc/rc in place
of the rather klunky 'asktime'.  It reads the cmos clock and sets
the system time to the second.  The program returns exit status 0
if it succeeds, 1 for most failures (stime() failure or /dev/cmos
read access failure), or 200 for the rare cases where the cmos
device appears to be acting up.

cmos_disp
--------------------------------------------------------------------
This is an experimental program that displays information contained
in the cmos ram.  It has some bugs in it (on my system, it reports
no floppy drives, when I in fact have one).  The information used
to build the program came from the sketchy notes in the IBM AT
Technical Reference Manual.  Look in header file 'at_cmos.h' and
source file 'cmos_disp.c' for notes I have made about empirical
"discoveries".

Acknowlegments
--------------------------------------------------------------------
The routines for converting mm/dd/yy hh:mm:ss to seconds since
the epoch were adapted from the 'tm_to_time.c' module in Emmet P.
Gray's conversion of arc to unix.

The concepts of parsing the Naval Observatory data stream came from
an anonymous author's nbs.c (which read the data stream from stdin).

The hayes modem handler came from my homebrew comm program I've
been hacking on the last couple of years.

And I'd like to say hello to Mom and everybody that's watching.
Enjoy.

NOT COPYRIGHTED BY or clutched to the chest of:
Warren H. Tucker
150 West Lake Drive
Mountain Park, GA.  30075
(404)587-5766

\Rogue\Monster\
else
  echo "will not over write ./README"
fi
if `test ! -s ./at_cmos.h`
then
echo "writing ./at_cmos.h"
cat > ./at_cmos.h << '\Rogue\Monster\'
/* CHK=0x7AFA */
/*+-------------------------------------------------------------------------
	at_cmos.h -- 286 cmos ram definitions

  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA 30075
  (404)587-5766

--------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-23-1988-19:06-wht-release */
/*:07-23-1988-15:12-wht-break out from cmos_disp.c */

#ifndef uchar
#define uchar	unsigned char
#endif

typedef struct at_cmos
{
	uchar	sec;
	uchar	sec_alarm;
	uchar	min;
	uchar	min_alarm;
	uchar	hour;
	uchar	hour_alarm;
	uchar	day;			/* 0-6 */
	uchar	date;			/* 1-31 */
	uchar	month;
	uchar	year;
	uchar	srA;			/* status register A */
	uchar	srB;			/* status register B */
	uchar	srC;			/* status register C */
	uchar	srD;			/* status register D */

	uchar	dsb;			/* diagnostic status byte */
	uchar	ssb;			/* shutdown status byte */
	uchar	ddtb;			/* diskette drive type byte */
	uchar	rsvd11;			/* byte at address 0x11 reserved */
	uchar	fdtb;			/* fixed disk type byte */
	uchar	rsvd13;			/* byte at address 0x13 reserved */
	uchar	equip;			/* equipment byte */

/* base size of memory = (contents(byte 0x16) << 8) + contents(byte 0x15) */
	uchar	base_low;		/* low base memory byte */
	uchar	base_high;		/* hi base memory byte */
	
/* memory expansion size similar calculation (0x17 low order byte, 0x18 hi) */
	uchar	expand_low;
	uchar	expand_high;

/* reserved bytes */
	uchar	rsvd19_2D[ 0x2D - 0x19 + 1 ];

/* checksum (on bytes in addresses 0x10 - 0x20) */
	uchar	cksum_high;		/* tech ref sez these bytes in other order */
	uchar	cksum_low;

/* > 1 mb expansion size (in cmos ram addresses 0x30-31) */
	uchar	exp2_low;
	uchar	exp2_high;

	uchar	century;		/* date century byte */
	uchar	info_flag;		/* see IF_ bits below */

	uchar	rsvd34_3F[ 0x3F - 0x34 + 1];

} AT_CMOS;

union cmos_union
{
	uchar		a[64];
	AT_CMOS		s;
};

/*+-----------------------------------------------------------------------
	AT Real Time Clock Status Register bit assignments
------------------------------------------------------------------------*/
#define SRA_UIP		0x80	/* update in progress */
#define SRA_DV22	0x70	/* 3-bit time-base divider */
#define SRA_DVOUT	0x0F	/* 4-bit time-base divider output freq */

#define SRB_SET		0x80	/* when 1, no tick advance (set mode) */
#define SRB_PIE		0x40	/* when 1, periodic interrupt */
#define SRB_AIE		0x20	/* when 1, alarm enabled */
#define SRB_UIE		0x10	/* enabled update-ended interrupt */
#define SRB_SQWE	0x08	/* square wave enabled */
#define SRB_DM		0x04	/* data mode 1==binary, 0==BCD */
#define SRB_24HR	0x02	/* 1==24 hour mode, 0==12 hour mode */
#define SRB_DSE		0x01	/* daylight savings time enabled */

#define SRC_IRQF	0x80
#define SRC_PF		0x40
#define SRC_AF		0x20
#define SRC_UF		0x10
							/* remaining bits in status register C reserved */

#define SRD_VRB		0x80	/* valid ram (==0 when power has been lost)
							 * remaining bits in status register D reserved */


/*+-----------------------------------------------------------------------
	Diagnostic Status Byte
------------------------------------------------------------------------*/
#define DSB_RTLOST	0x80	/* ==1 if real time clock chip lost power */
#define DSB_CSI		0x40	/* checksum status indicator == 1 if bad */
#define DSB_ICI		0x20	/* invalid configuration information (if ==1) */
#define DSB_MSM		0x10	/* memory size miscompare (==1 if different) */
#define DSB_HDBAD	0x08	/* fixed disk bad if == 1 */
#define DSB_TSI		0x04	/* time status indicator (==1 if time bad) */

#ifdef NEEDED	/* not under XENIX! */
#define		cmos_ctrl_dev		0x70		/* cmos address control port */
#define		cmos_data_dev		0x71		/* cmos data i/o port */
#endif

\Rogue\Monster\
else
  echo "will not over write ./at_cmos.h"
fi
if `test ! -s ./cmos_disp.c`
then
echo "writing ./cmos_disp.c"
cat > ./cmos_disp.c << '\Rogue\Monster\'
/* CHK=0xD818 */
/*+-------------------------------------------------------------------------
	cmos_disp.c -- interprets /dev/cmos 286 CMOS ram

  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA 30075
  (404)587-5766
--------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-23-1988-19:05-wht-release */
/*:07-23-1988-14:17-wht-BIG face life on old program */

#include <stdio.h>
#include <fcntl.h>
#include "at_cmos.h"
#include "dev_cmos.h"

union cmos_union	cmos;

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

char	*cmosname = "/dev/cmos";

main(argc,argv)
int		argc;
char	**argv;
{
register int icmos;
register int itmp;
char	*format;
int		fdcmos;
int		clock_bcd_mode;			/* true if clock in bcd mode */
unsigned short calc_cksum;		/* calculated checksum */
unsigned short cmos_cksum;		/* checksum in cmos */

	if((fdcmos = open(cmosname,O_RDONLY)) < 0)
	{
		perror(cmosname);
		exit(1);
	}
	get_cmos(fdcmos,&cmos);
	close(fdcmos);

	for(icmos = 0; icmos < 64; icmos++)
	{
		if(icmos % 16 == 0)
			printf("\n %02x:  ",icmos);
		printf("%02x ",cmos.a[icmos]);
		if(icmos % 4 == 3)
			printf(" ");
	}
	printf("\n\n");

	clock_bcd_mode = (cmos.s.srB & SRB_DM) ? 0 : 1;

	printf("Base memory:       %6dk  (%8lx bytes hex)\n",
					itmp = cmos.s.base_high << 8 | cmos.s.base_low,
					(long)itmp * (long)1024);

	printf("Expansion memory:  %6dk  (%8lx bytes hex)\n",
					itmp = cmos.s.expand_high << 8 | cmos.s.expand_low,
					(long)itmp * (long)1024);

	printf("Memory above 1MB:  %6dk  (%8lx bytes hex)\n",
					itmp = cmos.s.exp2_high << 8 | cmos.s.exp2_low,
					(long)itmp * (long)1024);

	printf("\n");

	printf("Diag status byte: %02x  Shutdown status byte: %02x\n",
					cmos.s.dsb,cmos.s.ssb);

	if(cmos.s.dsb)
	{
		itmp = cmos.s.dsb;
		printf("  Diagnostic Status Byte (DSB) says:\n");
		if(itmp & 0x80)
			printf("      Real-time clock has lost power.\n");
		if(itmp & 0x40)
			printf("      Bad configuration record checksum.\n");
		if(itmp & 0x20)
			printf("      Incorrect configuration information.\n");
		if(itmp & 0x10)
			printf("      Memory size miscompare.\n");
		if(itmp & 0x08)
			printf("      Fixed disk adapter failed init.\n");
		if(itmp & 0x04)
			printf("      Time is invalid.\n");
		printf("\n");
	}

	printf("Clock: %d-hour mode, %s time, alarm %s, %s mode\n",
					(cmos.s.srB & SRB_24HR) ? 24 : 12,
					(cmos.s.srB & SRB_DSE) ? "daylight" : "standard",
					(cmos.s.srB & SRB_AIE) ? "enabled" : "disabled",
					(clock_bcd_mode) ? "bcd" : "hex");

	if(clock_bcd_mode)
		format = "Date: %02x-%02x-%02x%02x  ";
	else
		format = "Date: %02d-%02d-%02d%02d  ";
	printf(format,cmos.s.month,cmos.s.date,cmos.s.century,cmos.s.year);

	if(clock_bcd_mode)
		format = "Time: %02x:%02x:%02x  Alarm: %02x:%02x:%02x\n";
	else
		format = "Time: %02d:%02d:%02d  Alarm: %02d:%02d:%02d\n";
	printf(format,cmos.s.hour,cmos.s.min,cmos.s.sec,
		cmos.s.hour_alarm,cmos.s.min_alarm,cmos.s.sec_alarm);

	printf("Number of diskette drives: %d   Math coprocessor %s\n",
					(int)(cmos.s.equip & 0xC0) >> 6,
					(cmos.s.equip & 0x02) ? "present" : "not present"
					);

	printf("Primary display:  ");
	switch( itmp = (cmos.s.equip & 0x30) >> 4)
	{
		case 1:
			format = "Color/Graphics 40 column\n";
			break;
		case 2:
			format = "Color/Graphics 80 column\n";
			break;
		case 3:
			format = "Monochrome display\n";
			break;
		default:
			format = "unknown type: %d\n";
			break;
	}
	printf(format,itmp);

/* tech ref doesn't say: empirically determined checksum algorithm (correct?) */
	calc_cksum = 0;
	for(itmp = 0x10; itmp < 0x21; itmp++)
		calc_cksum += cmos.a[itmp];
	calc_cksum++;

	cmos_cksum = (cmos.s.cksum_high << 8) | cmos.s.cksum_low;
	printf("Checksum: in ram: %04x  calculated: %04x\n",cmos_cksum,calc_cksum);

	exit(0);	/* for now */
	
}	/* end of main() */

\Rogue\Monster\
else
  echo "will not over write ./cmos_disp.c"
fi
if `test ! -s ./dev_cmos.c`
then
echo "writing ./dev_cmos.c"
cat > ./dev_cmos.c << '\Rogue\Monster\'
/* CHK=0x2A78 */
/*+-------------------------------------------------------------------------
	dev_cmos.c -- XENIX /dev/cmos routines

  Defined functions:
	bcdch_to_uchar(bcdch)
	get_clock(fdcmos,year,month,day,hour,min,sec)
	get_cmos(fdcmos,cmosbuf)
	set_clock(fdcmos,year,month,day,hour,min,sec)
	uchar_to_bcdch(uch)

  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA 30075
  (404)587-5766

--------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-23-1988-19:06-wht-release */
/*:07-23-1988-13:53-wht-creation */

#include "at_cmos.h"
#include "dev_cmos.h"

long seek(int,long,int);
int read(int,char *,int);

union cmos_union cu;

#define SEEKPOS(elem)	((long)((char *)&cu.s.elem - (char *)&cu))

/*+-------------------------------------------------------------------------
	bcdch_to_uchar(bcdch) -- convert two-nibble bcd to binary
--------------------------------------------------------------------------*/
unsigned char
bcdch_to_uchar(bcdch)
unsigned char bcdch;
{
	return((((bcdch & 0xF0) >> 4) * 10) + (bcdch & 0x0F));
}	/* end of bcdch_to_uchar */

/*+-------------------------------------------------------------------------
	uchar_to_bcdch(uch) -- convert binary to two-nibble bcd
--------------------------------------------------------------------------*/
unsigned char
uchar_to_bcdch(uch)
unsigned char uch;
{
	return(((uch / 10) << 4) | (uch % 10));
}	/* end of uchar_to_bcdch */

/*+-------------------------------------------------------------------------
	get_cmos(fdcmos,cmosbuf)
--------------------------------------------------------------------------*/
void
get_cmos(fdcmos,cmosbuf)
int		fdcmos;
union cmos_union *cmosbuf;
{
unsigned int wait_counter;
unsigned char stat_reg_A;

/* check for clock update in progress and wait til done */
	wait_counter = 32768;
	while(1)
	{
		if(--wait_counter == 0)
		{
			puts("/dev/cmos: clock update in progress for too long");
			exit(200);
		}
		
		lseek(fdcmos,SEEKPOS(srA),0);		/* position to status register A */
		read(fdcmos,&stat_reg_A,1);
		if(!(stat_reg_A & SRA_UIP))			/* check update bit */
			break;							/* and break out of loop if not */
	}

/* read the cmos ram */
	lseek(fdcmos,0L,0);
	read(fdcmos,(char *)cmosbuf,sizeof(AT_CMOS));

}	/* end of get_cmos */

/*+-------------------------------------------------------------------------
	get_clock(fdcmos,year,month,day,hour,min,sec) -- read /dev/cmos clock

fdcmos open fd to /dev/cmos
return values:
year is full year (e.g., 1988)
month is 0-11
day is 0-30
hour is 0-23
min is 0-59
--------------------------------------------------------------------------*/
void
get_clock(fdcmos,year,month,day,hour,min,sec)
int		fdcmos;
int		*year;
int		*month;
int		*day;
int		*hour;
int		*min;
int		*sec;
{
char	clkstr[12];

	get_cmos(fdcmos,&cu);

	*year  = (int)bcdch_to_uchar(cu.s.year) + 1900;
	*month = (int)bcdch_to_uchar(cu.s.month) - 1;
	*day   = (int)bcdch_to_uchar(cu.s.date) - 1;
	*hour  = (int)bcdch_to_uchar(cu.s.hour);
	*min   = (int)bcdch_to_uchar(cu.s.min);
	*sec   = (int)bcdch_to_uchar(cu.s.sec);

}	/* end of get_clock */

/*+-------------------------------------------------------------------------
	set_clock(fdcmos,year,month,day,hour,min,sec) -- write /dev/cmos clock
fdcmos open fd to /dev/cmos
other values: as returned by localtime()
--------------------------------------------------------------------------*/
void
set_clock(fdcmos,year,month,day,hour,min,sec)
int		fdcmos;
int		year;
int		month;
int		day;
int		hour;
int		min;
{
unsigned char stat_reg_B;
unsigned char wchar;

	month++;			/* month to 1-n */
	year %= 100;		/* think ahead (ha) */

/* set no tick advance */
	lseek(fdcmos,SEEKPOS(srB),0);
	read(fdcmos,&stat_reg_B,1);		/* get current value */
	stat_reg_B |= SRB_SET;
	lseek(fdcmos,SEEKPOS(srB),0);
	write(fdcmos,&stat_reg_B,1);	/* plug new value */

/* set clock */
	wchar = uchar_to_bcdch((uchar)year);
	lseek(fdcmos,SEEKPOS(year),0);
	write(fdcmos,&wchar,1);

	wchar = uchar_to_bcdch((uchar)month);
	lseek(fdcmos,SEEKPOS(month),0);
	write(fdcmos,&wchar,1);

	wchar = uchar_to_bcdch((uchar)day);
	lseek(fdcmos,SEEKPOS(date),0);
	write(fdcmos,&wchar,1);

	wchar = uchar_to_bcdch((uchar)hour);
	lseek(fdcmos,SEEKPOS(hour),0);
	write(fdcmos,&wchar,1);

	wchar = uchar_to_bcdch((uchar)min);
	lseek(fdcmos,SEEKPOS(min),0);
	write(fdcmos,&wchar,1);

	wchar = uchar_to_bcdch((uchar)sec);
	lseek(fdcmos,SEEKPOS(sec),0);
	write(fdcmos,&wchar,1);
	
/* set no tick advance */
	lseek(fdcmos,SEEKPOS(srB),0);
	stat_reg_B &= ~SRB_SET;
	write(fdcmos,&stat_reg_B,1);	/* plug new value */

}	/* end of set_clock */

\Rogue\Monster\
else
  echo "will not over write ./dev_cmos.c"
fi
if `test ! -s ./dev_cmos.h`
then
echo "writing ./dev_cmos.h"
cat > ./dev_cmos.h << '\Rogue\Monster\'
/* CHK=0x843A */
/*+-------------------------------------------------------------------------
	dev_clock.h -- prototypes for dev_clock.c functions

  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA 30075
  (404)587-5766

--------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-23-1988-19:07-wht-release */
/*:07-23-1988-13:55-wht-creattion */

unsigned char bcdch_to_uchar(unsigned char );
unsigned char uchar_to_bcdch(unsigned char );
void get_cmos(int ,union cmos_union  *);
void get_clock(int ,int  *,int  *,int  *,int  *,int  *,int  *);
void set_clock(int ,int ,int ,int ,int ,int ,int );
\Rogue\Monster\
else
  echo "will not over write ./dev_cmos.h"
fi
if `test ! -s ./tags`
then
echo "writing ./tags"
cat > ./tags << '\Rogue\Monster\'
bcdch_to_uchar dev_cmos.c ?^bcdch_to_uchar(bcdch)$?
create_lock_file NBSsetclk.c ?^create_lock_file(lock_file_name)$?
date_to_epochsecs CMOSstime.c ?^date_to_epochsecs(year,month,day,hour,min,sec)$?
days_since_epoch CMOSstime.c ?^days_since_epoch(year,month,day)$?
get_clock dev_cmos.c ?^get_clock(fdcmos,year,month,day,hour,min,sec)$?
get_cmos dev_cmos.c ?^get_cmos(fdcmos,cmosbuf)$?
hangup NBSsetclk.c ?^hangup(sig)$?
hayes_dial NBSsetclk.c ?^hayes_dial()$?
hayes_send_cmd NBSsetclk.c ?^hayes_send_cmd(cmd)$?
lclose NBSsetclk.c ?^lclose()$?
leap_year CMOSstime.c ?^leap_year(yr)$?
lgetc NBSsetclk.c ?^lgetc(char_rtnd)$?
lgetc_timeout NBSsetclk.c ?^lgetc_timeout(timeout_msec)$?
lgets_timeout NBSsetclk.c ?^lgets_timeout(lrwt)$?
lkill_buf NBSsetclk.c ?^lkill_buf()$?
lock_tty NBSsetclk.c ?^lock_tty()$?
lopen NBSsetclk.c ?^lopen()$?
lputc NBSsetclk.c ?^lputc(lchar)$?
lputs_paced NBSsetclk.c ?^lputs_paced(pace_msec,string)$?
lrdchk NBSsetclk.c ?^lrdchk()$?
lset_baud_rate NBSsetclk.c ?^lset_baud_rate(ioctl_flag)$?
lset_parity NBSsetclk.c ?^lset_parity(ioctl_flag)$?
main CMOSstime.c ?^main(argc,argv,envp)$?
main NBSsetclk.c ?^main(argc,argv,envp)$?
main cmos_disp.c ?^main(argc,argv)$?
make_lock_name NBSsetclk.c ?^make_lock_name(ttyname,lock_file_name)$?
other_lock_name NBSsetclk.c ?^other_lock_name(first_lock_name)$?
set_clock dev_cmos.c ?^set_clock(fdcmos,year,month,day,hour,min,sec)$?
to_lower NBSsetclk.c ?^char to_lower(ch)$?
to_upper NBSsetclk.c ?^char to_upper(ch)$?
uchar_to_bcdch dev_cmos.c ?^uchar_to_bcdch(uch)$?
ulcmpb NBSsetclk.c ?^ulcmpb(str1,str2)$?
ulindex NBSsetclk.c ?^int ulindex(str1,str2)$?
unlock_tty NBSsetclk.c ?^unlock_tty()$?
usage NBSsetclk.c ?^usage()$?
valid_baud_rate NBSsetclk.c ?^valid_baud_rate(baud)$?
\Rogue\Monster\
else
  echo "will not over write ./tags"
fi
echo "Finished archive 1 of 1"
exit