#define VERSION "1.0"

#define EARLIEST_LIB	37	/* 2.04 and above */

/*
 *
 * ClockTool.c
 *
 *  Displays/adjusts/sets times of battery clock and/or system clock
 *  from one or the other.
 *
 * Options:
 *
 *	-b       ; battery clock
 *	-s       ; system  clock
 *	-annnn   ; adjust battery clock time by -nnnn millisecs",
 *	+annnn   ; adjust battery clock time by +nnnn millisecs",
 *	-l<file> ; log system time to <file> , (default LOG_CLOCKTOOL:)
 *	-n<str>  ; append the <str> to log file entry, e.g. "reboot"
 *
 *  (see Usage () )
 *
 * Runs under AmigaDOS 2.04+, and only machines containing
 * a battery clock.
 *
 *
 *
 *****	Written by Gary Duncan
 *
 *	Bug reports etc via e-mail to gduncan@philips.oz.au) , or mail to
 *
 *Work:	Gary Duncan
 *	Philips PTS
 *      23 Lakeside Dr
 *	Tally-Ho Technology Park
 *	Burwood East Vic 3151
 *	Australia
 *
 *Home:	Gary Duncan
 *	57 Melbourne Hill Rd
 *	Warrandyte
 *	Vic 3113
 * 	Australia
 *
 *
 *
 *****	Freely distributable for non-commercial purposes.
 *	Please keep this header intact.
 *
 *
 *	Compiles under SAS 6.2
 *
 *	Formatted with 'indent -gnu' ; a big time-saving program.
 *
 * Functions:-

	main
	tv2dss
	tv2str
	dss2tv
	dss2str
	str2dss
	Init_Timer
	hexit
	DeleteTimer
	usage
	scan_args
	tv_diff
	tv_diff_str
	format_diff_str
	set_system_time
	get_system_time
	get_battclock_time
	set_battclock_time
	tv_update
	check4battclock
	get_syncd_batt_time
	get_syncd_sys_time
	print_clock
	display_clocks
	adjust_clock
	set_syncd_system_time
	set_syncd_battclock_time
	wait_for_timer
	pause

 */

#include "ClockTool.h"


/*
 * string for AmigaDOS Version Command
 */
char A_vers[] = "\0$VER: clocktool\t" VERSION " (" __DATE__ " " __TIME__ ")";


LIBRARY *UtilityBase = NULL;
LIBRARY *BattClockBase;

TIMEVAL tv_sysclock;
TIMEVAL tv_battclock;
TIMEVAL tv_tmp;

DATESTAMP dss_sysclock;
DATESTAMP dss_battclock;

LONG adjust;
LONG temp;

MSGPORT *TimeP = NULL;
TIMERREQUEST *Timermsg;
IOREQUEST *Timer = NULL;

BOOL a_flag = FALSE;
BOOL b_flag = FALSE;
BOOL l_flag = FALSE;
BOOL n_flag = FALSE;
BOOL s_flag = FALSE;

char buf[80];
char *xstr = "";

int opt_count = 0;

char *logfile;
FILE *fp = NULL;
char log_buf[80];

/*
 * used by date conversion functions
 */

DM days_month[] =
{
  31, "Jan",
  28, "Feb",
  31, "Mar",
  30, "Apr",
  31, "May",
  30, "Jun",
  31, "Jul",
  31, "Aug",
  30, "Sep",
  31, "Oct",
  30, "Nov",
  31, "Dec",
};

/*
 *********************************************************************
 */

VOID
main (int argc, char **argv)
{

  /*
   * abort if no battclock and not AmigaDos 2.xx
   */
  if (check4battclock () == FALSE)
    {
      hexit (1);
    }

  scan_args (argc, argv);

  Init_Timer ();

  /*
   ***** Mode (1) ? - just display both clocks ?
   */
  if (opt_count == 0)
    {
      get_syncd_batt_time (&tv_battclock);
      get_system_time (&tv_sysclock);

      display_clocks (&tv_battclock, &tv_sysclock);
      hexit (1);
    }
  /*
   * check that there is at least only one b or s option
   */

  if ((b_flag == TRUE) && (s_flag == TRUE))
    {
      usage ("both b and s options set");
      hexit (1);
    }

  if ((b_flag == FALSE) && (s_flag == FALSE))
    {
      usage ("need either b or s option");
      hexit (1);
    }

  /*
   ***** Mode (2) -  just a s or b option ?
   */

  if (opt_count == 1)
    {
      /*
       * just set one clock from the other
       */
      if (s_flag == TRUE)
	{
	  (void) set_syncd_system_time ();

	  print_old_new ("system", &tv_sysclock, &tv_battclock);
	}
      else
	/* set battclock from system time */
	{
	  (void) set_syncd_battclock_time ();

	  print_old_new ("battery", &tv_sysclock, &tv_battclock);
	}

      hexit (1);
    }

  /*
   ***** Mode (3) - adjust a clock ?
   */
  if ((n_flag == TRUE) && (l_flag == FALSE))
    {
      usage ("need l option");
      hexit (1);
    }

  if (a_flag == TRUE)
    {
      adjust_clock (adjust, &tv_tmp);
    }

  if (l_flag == FALSE)
    {
      if (s_flag == TRUE)
	{
	  print_old_new ("system", &tv_sysclock, &tv_tmp);
	}
      else
	{
	  print_old_new ("battery", &tv_battclock, &tv_tmp);
	}
      hexit (1);
    }

  /*
   ***** Mode (4) - log selected clock time  ?
   */


  /*
   * we have l option here...
   */

  if (s_flag == TRUE)
    {
      temp = set_syncd_system_time ();

      sprintf (log_buf, "S: %s %s", tv2str (&tv_sysclock),
	       format_diff_str (temp, "(%s secs)  "));
    }
  else
    /* set battclock from system time */
    {
      temp = set_syncd_battclock_time ();

      sprintf (log_buf, "B: %s %s", tv2str (&tv_battclock),
	       format_diff_str (temp, "(%s secs)  "));
    }

  if (n_flag == TRUE)
    {
      strcat (log_buf, xstr);
    }

  strcat (log_buf, "\n");

  printf ("%s", log_buf);

#if 1
  /*
   * now log message , maybe
   */
  if (fp)
    {
      int rote;
      int len = strlen (log_buf);

      if ((rote = fwrite (log_buf, 1, len, fp)) != len)
	{
	  fprintf (stderr, "fwrite error (wanted=%d),(actual=%d)\n",
		   len, rote);
	}
    }
#endif

  hexit (1);
}


/***************************************************************************


  Function :  	tv2dss

  Purpose:	given a Timeval structure , returns Datestamp

  Entry    :	ptr to Timeval
		ptr to Datestamp

  Returns  :


****************************************************************************/


void
tv2dss (TIMEVAL * tv, DATESTAMP * dss)

{
  long rem;

  dss->ds_Days = tv->tv_secs / (24 * 60 * 60);
  rem = tv->tv_secs % (24 * 60 * 60);	/* secs in last day */

  dss->ds_Minute = rem / 60;
  rem = rem % 60;		/* secs in last minute */

  rem = (rem * 1000) + (tv->tv_micro / 1000);	/* msecs in last minute */

  dss->ds_Tick = rem / (1000 / TICKS_PER_SECOND);	/* ticks in last minute */
}

/***************************************************************************


  Function :  	tv2str

  Purpose:	given a Timeval structure , returns date string

  Entry    :	ptr to Timeval

  Returns  :


****************************************************************************/


char *
tv2str (TIMEVAL * tv)

{
  static char tbuf[40];
  DATESTAMP dss;

  tv2dss (tv, &dss);
  dss2str (&dss, tbuf);
  return tbuf;
}

/***************************************************************************


  Function :   	dss2tv

  Purpose:	given a DateStamp structure , returns an updated TIMEVAL

  Entry    :    ptr to DateStamp structure

  Returns  :


****************************************************************************/


void
dss2tv (DATESTAMP * t1, TIMEVAL * tv)

{
  ULONG secs;

  secs = t1->ds_Days * 24 * 60 * 60;
  secs += t1->ds_Minute * 60;
  secs += t1->ds_Tick / TICKS_PER_SECOND;

  tv->tv_secs = secs;
  tv->tv_micro = (t1->ds_Tick % TICKS_PER_SECOND) * (1000000 / TICKS_PER_SECOND);

}

/***************************************************************************


  Function :  	dss2str

  Purpose:	generates a date string given a DateStamp

  Entry    :	ptr to Datestamp


  Returns  :    ptr to date string



****************************************************************************/


void
dss2str (DATESTAMP * dss, char *str)

{
  static TIMEVAL tv;
  struct tm Time;
  int j;
  long time, ago;


  dss2tv (dss, &tv);		/* date to secs/usecs since 01 Jan 78 */
  time = tv.tv_secs;

  Time.tm_sec = time % 60;
  time /= 60;
  Time.tm_min = time % 60;
  time /= 60;
  Time.tm_hour = time % 24;
  time /= 24;
  Time.tm_wday = time % 7;
  Time.tm_year = 78 + (time / (4 * 365 + 1)) * 4;
  time %= 4 * 365 + 1;


  while (time)
    {
      ago = 365;
      if ((Time.tm_year & 3) == 0)	/* leap year */
	ago++;
      if (time < ago)
	break;
      time -= ago;
      Time.tm_year++;
    }
  Time.tm_yday = ++time;

  for (j = 0; j < 12; j++)
    {
      ago = days_month[j].days;
      if (j == 1 && (Time.tm_year & 3) == 0)	/* Feb , and leap year ? */
	ago++;
      if (time <= ago)
	break;
      time -= ago;
    }
  Time.tm_mon = j;
  Time.tm_mday = time;

  /* date format : 23-dec-88 22:33:01  */

  sprintf (str, "%02d-%s-%2d %02d:%02d:%02d.%03d",
	   Time.tm_mday,
	   days_month[Time.tm_mon].months,
	   Time.tm_year,
	   Time.tm_hour,
	   Time.tm_min,
	   Time.tm_sec,
	   tv.tv_micro / 1000
    );

}



/***************************************************************************


  Function :   str2dss


  Purpose  :   converts a truncated Amiga date string to a Datestamp

		e.g. 	"22-Dec-92 08:10:26" , no leading day
  Entry    :


  Returns  :   TRUE if succssful, FALSE otherwise



****************************************************************************/

BOOLEAN
str2dss (char *str, DATESTAMP * ds)

{
/* 012345678901234567 */
/* 22-Dec-92 08:10:26 */

  char *p = str;
  ULONG dss_days;

  int day, month, year, hour, min, sec, diy, j, k, rem;

  p[2] = '\0';
  p[6] = '\0';
  p[9] = '\0';
  p[12] = '\0';
  p[15] = '\0';

  day = atoi (&p[0]);
  year = atoi (&p[7]) + 1900;
  hour = atoi (&p[10]);
  min = atoi (&p[13]);
  sec = atoi (&p[16]);

  /*
   * compute days-in-year from month
   * - may be incremented later if a leap year
   */
  for (p = str + 3, j = 0, diy = 0;; ++j)
    {
      if (j == 12)
	return FALSE;
      if (strcmp (p, days_month[j].months) == 0)
	{
	  month = j + 1;
	  break;
	}
      diy += days_month[j].days;
    }

  /*
   * tricky bit ; adjust for leap years
   */
  dss_days = diy + day - 1;
  dss_days += 2 * 365;		/* 1978, 1979 */
  for (j = 1980, k = 0;;)
    {
      dss_days += 365;
      if ((rem = (k++ % 4)) == 0)
	dss_days += 1;		/* leap year */

      if (++j == year)
	{
	  if (((rem = (k++ % 4)) == 0) && (month > 2))
	    {
	      dss_days += 1;
	    }
	  break;
	}
    }

  ds->ds_Days = dss_days;
  /*
   * now fill in mins, ticks in min
   */
  ds->ds_Minute = (60 * hour) + min;
  ds->ds_Tick = sec * TICKS_PER_SECOND;

  return TRUE;
}

/***************************************************************************


  Function :	Init_Timer

  Purpose:	Inits timer

  Entry    :


  Returns  :



****************************************************************************/


void
Init_Timer (void)
{

  int error;

  TimeP = CreatePort (0, 0);
  if (TimeP == NULL)
    hexit (1);

  Timermsg = (TIMERREQUEST *) CreateExtIO (TimeP, sizeof (TIMERREQUEST));
  if (Timermsg == NULL)
    {
      hexit (1);
    }

  error = OpenDevice (TIMERNAME, UNIT_MICROHZ, (IOREQUEST *) Timermsg, 0);
  if (error != 0)
    {
      DeleteExtIO ((IOREQUEST *) Timermsg);
      hexit (1);
    }
}


/***************************************************************************


  Function :      hexit


  Purpose  :      cleans up and exits


  Entry    :


  Returns  :



****************************************************************************/


void
hexit (int fred)
{

  if (UtilityBase)
    {
      CloseLibrary (UtilityBase);
    }

  if (Timermsg != NULL)
    {
      DeleteTimer (Timermsg);
    }
  /*
   * close log file if open
   */
  if (fp)
    fclose (fp);

  exit (0);
}

/***************************************************************************


  Function :   	DeleteTimer

  Purpose:	Deletes Timer

  Entry    :


  Returns  :



****************************************************************************/


void
DeleteTimer (TIMERREQUEST * tr)
{
  MSGPORT *tp;

  tp = tr->tr_node.io_Message.mn_ReplyPort;
  if (tr != 0)
    {
      CloseDevice ((IOREQUEST *) tr);
      DeleteExtIO ((IOREQUEST *) tr);
    }
  if (tp != 0)
    DeletePort (tp);
}

/***************************************************************************


  Function :  	usage

  Purpose:	prints invocation parameters

  Entry    :


  Returns  :



****************************************************************************/

void
usage (char *p)


{
  static char *rats[] =
  {
    "Usage modes :-",
    " 1) display clocks : clocktool",
    " 2) set clock from",
    "     other clock   : clocktool -[b | s]",
    " 3) adjust clock   : clocktool -[b | s] [+|-a<n>]",
    " 4) log    clock   : clocktool -[b | s] -l<file> [-n<str>] [+|-a<n>]",
    "\nOptions:",
    "	-b         : battery clock",
    "	-s         : system  clock",
    "	+|-a<n>    : change selected clock by + or - <n> millisecs",
    "",
    "	-l<file>   : log time of selected clock to <file>",
    "                     (default file =  LOG_CLOCKTOOL:)",
    "	-n<str>    : append the <str> to log file entry, e.g. \"reboot\"",
    NULL
  };
  int j = 0;
  char *ptr;



  if (*p)
    printf ("Error: %s\n\n", p);

  printf (
  "%s: Displays/adjusts/sets times of battery clock and/or system clock\n\n",
	   "ClockTool");
  printf (
	   "Version %s [%s : %s] written by gduncan\n\n",
	   VERSION, __DATE__, __TIME__);

  while (ptr = rats[j++])
    printf ("%s\n", ptr);
}

/***************************************************************************


  Function :    	scan_args

  Purpose:	scans and validates command line

  Entry    :


  Returns  :



****************************************************************************/



void
scan_args (int argc, char **argv)

{
  char *ptr;
  char arch;
  char plusminus;

  while (--argc)
    {
      ptr = *++argv;
      plusminus = *ptr++;
      /*
       * now look for options
       */
      if ((plusminus == '-') || (plusminus == '+'))
	{
	  ++opt_count;
	  arch = *ptr++;

	  switch (arch)
	    {

	    case 'a':		/* adjust clock time */
	      a_flag = TRUE;
	      if (sscanf (ptr, "%d", &adjust) != 1)
		{
		  fprintf (stderr, "bad -a option value\n");
		  hexit (1);
		}
	      if (plusminus == '-')
		adjust = -adjust;

	      break;

	    case 'b':
	      b_flag = TRUE;
	      break;

	    case 's':
	      s_flag = TRUE;
	      break;

	    case 'h':		/*  	*/
	      usage ("");	/* help	*/
	      hexit (1);

	    case 'l':
	      l_flag = TRUE;
	      logfile = (*ptr) ? ptr : "LOG_CLOCKTOOL:";
	      if ((fp = fopen (logfile, "a")) == NULL)
		{
		  fprintf (stderr, "Can't open %s\n", logfile);
		  hexit (1);
		}
	      break;

	    case 'n':		/* put str in log file entry 	*/
	      n_flag = TRUE;
	      xstr = ptr;
	      break;

	    default:
	      usage ("unknown option");	/* bad option	*/
	      hexit (1);

	    }
	}
      else
	{
	  usage ("not an option");
	  hexit (1);
	}
    }
  if (b_flag && s_flag)
    {
      usage ("only one -b or -s option");	/* bad option	*/
      hexit (1);
    }
}

/***************************************************************************


  Function :  	tv_diff


  Purpose  :	returns difference in msecs between two TIMEVALS

  Entry    :


  Returns  :



****************************************************************************/

LONG
tv_diff (TIMEVAL * tv1, TIMEVAL * tv2)
{
  LONG val;

  val = ((LONG) tv2->tv_secs - (LONG) tv1->tv_secs) * 1000;
  val += (((LONG) tv2->tv_micro) - ((LONG) tv1->tv_micro)) / 1000;

  return val;
}

/***************************************************************************


  Function :  	tv_diff_str


  Purpose  :	returns difference in msecs between two TIMEVALS
		expressed as secs.msecs string

  Entry    :


  Returns  :



****************************************************************************/

char *
tv_diff_str (TIMEVAL * tv1, TIMEVAL * tv2)
{
  LONG val;

  val = tv_diff (tv1, tv2);

  return (format_diff_str (val, " (%s)"));
}

/***************************************************************************


  Function :  	format_diff_str


  Purpose  :	formats diff string

  Entry    :


  Returns  :



****************************************************************************/

char *
format_diff_str (LONG val, char *form)
{
  static char mbuf[80];
  char tmp[80];

  tmp[0] = ' ';

  if (val < 0)
    {
      tmp[0] = '-';
      val = -val;
    }
  else if (val > 0)
    {
      tmp[0] = '+';
    }
  if (val > 999999)
    {
      return " ( >999.999 ) ";
    }

  sprintf (&tmp[1], " %3d.%03d", val / 1000, val % 1000);

  sprintf (mbuf, form, tmp);
  return mbuf;
}

/***************************************************************************


  Function :   	set_system_time

  Purpose:	sets system date/time
		
  Entry    :	TIMEVAL


  Returns  :	void


****************************************************************************/

void
set_system_time (TIMEVAL * tv)
{

  Timermsg->tr_node.io_Command = TR_SETSYSTIME;
  Timermsg->tr_time.tv_secs = tv->tv_secs;
  Timermsg->tr_time.tv_micro = tv->tv_micro;

  if (DoIO ((IOREQUEST *) Timermsg))
    {
      fprintf (stderr, "set_system_time() - DoIO fail\n");
      hexit (1);
    }
}

/***************************************************************************


  Function :   	get_system_time

  Purpose:	gets system date/time
		
  Entry    :	ptr to TIMEVAL


  Returns  :	void


****************************************************************************/

void
get_system_time (TIMEVAL * tv)
{

  Timermsg->tr_node.io_Command = TR_GETSYSTIME;

  if (DoIO ((IOREQUEST *) Timermsg))
    {
      fprintf (stderr, "get_time() - DoIO fail\n");
      hexit (1);
    }
  tv->tv_secs = Timermsg->tr_time.tv_secs;
  tv->tv_micro = Timermsg->tr_time.tv_micro;
}

/***************************************************************************


  Function :   	get_battclock_time

  Purpose:	Get Battery Backed-up Clock time

  Entry    :    ptr to TIMEVAL structure

  Returns  :


****************************************************************************/


void
get_battclock_time (TIMEVAL * tv)

{

  tv->tv_secs = ReadBattClock ();
  tv->tv_micro = 0;
}

/***************************************************************************


  Function :   	set_battclock_time

  Purpose:	Set Battery Backed-up Clock time

  Entry    :    ptr to TIMEVAL structure

  Returns  :


****************************************************************************/


void
set_battclock_time (TIMEVAL * tv)

{
  WriteBattClock (tv->tv_secs);
}

/***************************************************************************


  Function :  	tv_update


  Purpose  :	updates a TIMEVAL with nnn msecs

  Entry    :


  Returns  :



****************************************************************************/

void
tv_update (TIMEVAL * tv, LONG msecs)
{
  LONG t_secs, t_msecs;
  TIMEVAL tv_tmp = *tv;
  BOOL flag = TRUE;

  if (msecs < 0)
    {
      flag = FALSE;
      msecs = -msecs;
    }
  t_secs = msecs / 1000;
  t_msecs = msecs % 1000;

  /*
   * conv microsecs field to msecs
   */
  tv_tmp.tv_micro %= 1000;

  if (flag == TRUE)
    {
      /* add */

      tv_tmp.tv_micro += t_msecs;
      if (tv_tmp.tv_micro >= 1000)
	{
	  tv_tmp.tv_micro %= 1000;
	  tv_tmp.tv_secs += 1;
	}
      tv_tmp.tv_secs += t_secs;
    }
  else
    {
      /* subtract */

      if (tv_tmp.tv_micro < t_msecs)
	{
	  tv_tmp.tv_micro += 1000 - t_msecs;
	  tv_tmp.tv_secs -= 1;
	}
      tv_tmp.tv_secs -= t_secs;
    }
  /*
   * conv  msecs back to microsecs
   */
  tv_tmp.tv_micro *= 1000;
  /*
   * update given TIMEVAL
   */

  *tv = tv_tmp;
}

/***************************************************************************


  Function :	check4battclock

  Purpose:	Inits other stuff

  Entry    :


  Returns  :



****************************************************************************/


BOOL
check4battclock (void)
{

  UtilityBase = (LIBRARY *) OpenLibrary ("utility.library", EARLIEST_LIB);

  if (UtilityBase == NULL)
    {
      fprintf (stderr, "Abort: only runs under AmigaDOS 2.04 and above\n");
      return FALSE;
    }
  /*
   * see if Battery Clock there
   */
  BattClockBase = OpenResource (BATTCLOCKNAME);
  if (BattClockBase == NULL)
    {
      fprintf (stderr, "Abort: can't find a Battery Clock\n");
      return FALSE;
    }
  return TRUE;
}

/***************************************************************************


  Function :	get_syncd_batt_time

  Purpose:	reads battclock and returns when sec just ticked over

  Entry    :


  Returns  :



****************************************************************************/

void
get_syncd_batt_time (TIMEVAL * tv)
{
  TIMEVAL tv_tmp;

  get_battclock_time (&tv_tmp);
  do
    {
      get_battclock_time (tv);
  } while (tv_tmp.tv_secs == tv->tv_secs);
}

/***************************************************************************


  Function :	get_syncd_sys_time

  Purpose:	reads sysclock and returns when sec just ticked over

  Entry    :


  Returns  :



****************************************************************************/

void
get_syncd_sys_time (TIMEVAL * tv)
{
  TIMEVAL tv_tmp;

  get_system_time (&tv_tmp);
  do
    {
      get_system_time (tv);
  } while (tv_tmp.tv_secs == tv->tv_secs);
}

/***************************************************************************


  Function :	print_clock

  Purpose:	prints clock time surrounded by given strings

  Entry    :


  Returns  :



****************************************************************************/

void
print_clock (char *pre, TIMEVAL * tv, char *post)
{

  /*
   * print results
   */
  printf ("%s%s%s", pre, tv2str (tv), post);
  fflush (stdout);
}

/***************************************************************************


  Function :	display both clocks

  Purpose:	as the name suggests

  Entry    :


  Returns  :



****************************************************************************/

void
display_clocks (TIMEVAL * tv_b, TIMEVAL * tv_s)
{

  print_clock ("Battery clock time = ", tv_b, "\n");
  print_clock ("System  clock time = ", tv_s, tv_diff_str (tv_b, tv_s));
  printf ("\n");

}

/***************************************************************************


  Function :	adjust a clock

  Purpose:	adjusts either system clock or  battclock by given
		number of millisecs

  Entry    :


  Returns  :



****************************************************************************/

void
adjust_clock (LONG msecs, TIMEVAL * tv)
{
  static TIMEVAL tv_tmpx;

  /*
   * wait until given clock wraps over a second
   */
  if (s_flag == TRUE)
    {
      get_syncd_sys_time (&tv_sysclock);
      tv_tmpx = tv_sysclock;
      tv_update (&tv_tmpx, msecs);

      set_system_time (&tv_tmpx);
    }
  else
    {
      LONG res;
      /*
       * battclock here
       */
      get_syncd_batt_time (&tv_battclock);
      tv_tmpx = tv_battclock;
      tv_update (&tv_tmpx, msecs);

      res = 1000 - (tv_tmpx.tv_micro / 1000);
      tv_update (&tv_battclock, 1000);
      ++tv_tmpx.tv_secs;

      /*
       * - wait a bit to account for fraction of a sec
       */
      pause (res);

      set_battclock_time (&tv_tmpx);
    }
  *tv = tv_tmpx;

}

/***************************************************************************


  Function :	print_old_new

  Purpose:	as the name suggests

  Entry    :


  Returns  :



****************************************************************************/

void
print_old_new (char *ptr, TIMEVAL * tv_old, TIMEVAL * tv_new)
{
  printf ("Old %s clock time = ", ptr);
  print_clock ("", tv_old, "\n");

  printf ("New %s clock time = ", ptr);
  print_clock ("", tv_new, tv_diff_str (tv_old, tv_new));
  printf ("\n");

}

/***************************************************************************


  Function :	set_syncd_system_time

  Purpose:	sets system clock to battclock time when it ticks
		over a second.

  Entry    :


  Returns  :



****************************************************************************/

LONG
set_syncd_system_time ()
{
  get_syncd_batt_time (&tv_battclock);
  get_system_time (&tv_sysclock);
  set_system_time (&tv_battclock);

  return (tv_diff (&tv_battclock, &tv_sysclock));
}

/***************************************************************************


  Function :	set_syncd_battclock_time

  Purpose:	sets battery clock to system time when it ticks
		over a second.

  Entry    :


  Returns  :



****************************************************************************/

LONG
set_syncd_battclock_time ()
{
  TIMEVAL tv_tmp;
  LONG diff;

  /*
   * we need to mess around a bit to get diff
   * between batt and system clock because batt clock
   * is only accurate to 1 sec
   */
  get_syncd_batt_time (&tv_battclock);
  get_system_time (&tv_tmp);
  diff = tv_diff (&tv_battclock, &tv_tmp);

  get_syncd_sys_time (&tv_sysclock);
  set_battclock_time (&tv_sysclock);

  /*
   * now correct battclock time to give msecs precision
   */
  tv_battclock = tv_sysclock;
  tv_update (&tv_battclock, diff);

  return (-diff);
}

/***************************************************************************


  Function :      wait_for_timer

  Purpose:	Waits until given time expires

  Entry    :


  Returns  :



****************************************************************************/


void
wait_for_timer (TIMERREQUEST * tr, TIMEVAL * tv)

{
  MSGPORT *tp;
  ULONG flags;

  tp = tr->tr_node.io_Message.mn_ReplyPort;

  tr->tr_node.io_Command = TR_ADDREQUEST;
  tr->tr_time.tv_secs = tv->tv_secs;
  tr->tr_time.tv_micro = tv->tv_micro;
  SendIO ((IOREQUEST *) tr);


  flags = Wait ((1L << tp->mp_SigBit) | SIGBREAKF_CTRL_C);
  if (flags & SIGBREAKF_CTRL_C)
    {
      AbortIO ((IOREQUEST *) tr);
      WaitIO ((IOREQUEST *) tr);
      hexit (1);
    }
}

/***************************************************************************


  Function :      pause


  Purpose  :      waits for given millisecs
		  ( or until ^C hit )

  Entry    :


  Returns  :



****************************************************************************/


void
pause (int millisecs)
{
  static TIMEVAL tv;

  tv.tv_secs = millisecs / 1000;
  tv.tv_micro = (millisecs % 1000) * 1000;

  wait_for_timer (Timermsg, &tv);
}

/**/
