#define VERSION "2.1 "

#define EARLIEST_LIB	37	/* 2.04 and above */
/* *INDENT-OFF* */
/**

 * 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
 *      -d<str>  ; set selected clock to str (dd-MMM-yy)
 *      -t<str>  ; set selected clock to str (hh:mm[:ss])
 *                    (use either or both d/t options)
 *      -annnn   ; adjust selected clock time by -nnnn millisecs",
 *      +annnn   ; adjust selected clock time by +nnnn millisecs",
 *      -l<file> ; log system time to <file> , reset clock from other
 *                                    (default LOG_CLOCKTOOL:)
 *      -L<file> ; log system time to <file> , Dont reset clock from other
 *                                    (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
 *
 *      57 Melbourne Hill Rd
 *      Warrandyte
 *      Vic 3113
 *      Australia
 *
 * E-mail: gduncan@werple.net.au
 *
 *
 *****  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_battclock_time
	get_syncd_system_time
	print_clock
	display_clocks
	adjust_clock
	set_system_time_to_syncd_battclock
	set_battclock_time_to_syncd_system_time
	wait_for_timer
	pause



 Version        Date    Changes
 ~~~~~~~        ~~~~    ~~~~~~~

 1.0            27Oct93 Original release
 1.1            04Apr94 Changes :-
                        1) use __AMIGADATE__
                        2) added L option, reorganised logic.
                        3) added d,t options (set date/time).
                        4) corrected clocktool -b output for big difference  
 1.1a           Comments added

 2.0		18Apr95	Exit with good/bad (0/1) values
 2.1		26Oct95 trivial changes to help
 **/

/* *INDENT-ON* */






#include "ClockTool.h"


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


LIBRARY *UtilityBase = NULL;
LIBRARY *BattClockBase;

TIMEVAL tv_sysclock;
TIMEVAL tv_battclock;
TIMEVAL tv_tmp;

DATESTAMP dss_sysclock;
DATESTAMP dss_battclock;

DATETIME datetime;
char *date_ptr;
char *time_ptr;

LONG adjust;
LONG temp;

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

BOOL a_flag = FALSE;
BOOL b_flag = FALSE;
BOOL d_flag = FALSE;
BOOL l_flag = FALSE;
BOOL L_flag = FALSE;
BOOL n_flag = FALSE;
BOOL s_flag = FALSE;
BOOL t_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);

  if (Init_Timer () == FALSE)
    {
      hexit (1);
    }

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

      display_clocks (&tv_battclock, &tv_sysclock);
      hexit (0);
    }

  /*
   ***** 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_system_time_to_syncd_battclock ();

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

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

      hexit (0);
    }

  /*
   ***** Mode (3) -  set date/time from command line ?
   */

  if ((d_flag == TRUE) || (t_flag == TRUE))
    {
      TIMEVAL t_val;

      DateStamp (&datetime.dat_Stamp);

      datetime.dat_Format = FORMAT_DOS;
      datetime.dat_Flags = 0;

      if (d_flag)
	{
	  datetime.dat_StrDate = date_ptr;
	  datetime.dat_StrTime = NULL;
	  if (StrToDate (&datetime) == FALSE)
	    {
	      fprintf (stderr, "invalid date string\n");
	      hexit (1);
	    }
	}
      if (t_flag)
	{
	  datetime.dat_StrTime = time_ptr;
	  datetime.dat_StrDate = NULL;
	  if (StrToDate (&datetime) == FALSE)
	    {
	      fprintf (stderr, "invalid time string\n");
	      hexit (1);
	    }
	}

      /*
       * now change date/time of selected clock
       */
      dss2tv (&datetime.dat_Stamp, &t_val);

      b_flag ? set_battclock_time (&t_val) : set_system_time (&t_val);

      /*
       * now display both clocks 
       */
      get_syncd_battclock_time (&tv_battclock);
      get_system_time (&tv_sysclock);

      display_clocks (&tv_battclock, &tv_sysclock);
      hexit (0);

    }

  /*
   ***** Mode (4) - adjust a clock (no logging) ?
   */

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

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

  /*
   ***** Mode (5) - log selected clock time with reset  ?
   */

  if (l_flag == TRUE)
    {
      if (s_flag == TRUE)
	{
	  temp = set_system_time_to_syncd_battclock ();

	  sprintf (log_buf, "\nS: %s %s",
		   tv2str (&tv_sysclock),
		   format_diff_str (temp, "(%s secs)  "));
	}
      else if (b_flag == TRUE)
	{
	  temp = set_battclock_time_to_syncd_system_time ();

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

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

      printf ("%s\n", log_buf);
    }

  /*
   ***** Mode (6) - log selected clock time with NO reset  ?
   */

  if (L_flag == TRUE)
    {

      get_syncd_battclock_time (&tv_battclock);
      get_system_time (&tv_sysclock);

      temp = tv_diff (&tv_sysclock, &tv_battclock);

      if (s_flag == TRUE)
	{
	  sprintf (log_buf, "\nS: %s %s", tv2str (&tv_sysclock),
		   format_diff_str (temp, "(%s secs)  "));
	}
      else if (b_flag == TRUE)
	{
	  sprintf (log_buf, "\nB: %s %s", tv2str (&tv_battclock),
		   format_diff_str (temp, "(%s secs)  "));

	}

      if (n_flag == TRUE)
	{
	  strcat (log_buf, xstr);
	}
#if 1
      printf ("%s\n", log_buf);
#endif
    }

  /*
   * 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);

	  hexit (99);
	}
    }

  hexit (0);
}


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


 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 successful, FALSE otherwise



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

BOOL
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  :



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


BOOL
Init_Timer (void)
{

  int error;

  TimeP = CreatePort (0, 0);
  if (TimeP == NULL)
    return FALSE;

  Timermsg = (TIMERREQUEST *) CreateExtIO (TimeP, sizeof (TIMERREQUEST));
  if (Timermsg == NULL)
    return FALSE;

  error = OpenDevice (TIMERNAME, UNIT_MICROHZ, (IOREQUEST *) Timermsg, 0);
  if (error != 0)
    {
      DeleteExtIO ((IOREQUEST *) Timermsg);
      return FALSE;
    }
  return TRUE;
}


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


 Function :      hexit


 Purpose  :      cleans up and exits


 Entry    :


 Returns  :



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


void
hexit (int exit_val)
{

  if (UtilityBase)
    {
      CloseLibrary (UtilityBase);
    }

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

  exit (exit_val);
}

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


 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 :     tv_diff


 Purpose  :     returns difference in msecs between two TIMEVALS,
 or -1 if overflow 
 Entry    :


 Returns  :



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

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

  val = (LONG) tv2->tv_secs - (LONG) tv1->tv_secs;
  /*
   * quick and nasty range check
   */
  if (labs (val) > 4294966L)
    return -1;

  val *= 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 == -1)
    {
      return " ( >999.999 ) ";
    }

  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 ("diskfont.library", 0L);


  if (UtilityBase == NULL)
    {
      fprintf (stderr, "Can't open diskfont.library\n");
      return FALSE;
    }

  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_battclock_time

 Purpose:       reads battclock and returns when sec just ticked over

 Entry    :


 Returns  :



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

void
get_syncd_battclock_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_system_time

 Purpose:       reads sysclock and returns when sec just ticked over

 Entry    :


 Returns  :



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

void
get_syncd_system_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_system_time (&tv_sysclock);
      tv_tmpx = tv_sysclock;
      tv_update (&tv_tmpx, msecs);

      set_system_time (&tv_tmpx);
    }
  else
    {
      LONG res;
      /*
         * battclock here
       */
      get_syncd_battclock_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_system_time_to_syncd_battclock

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

 Entry    :


 Returns  :



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

LONG
set_system_time_to_syncd_battclock ()
{
  get_syncd_battclock_time (&tv_battclock);
  get_system_time (&tv_sysclock);
  set_system_time (&tv_battclock);

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


 Function :     set_battclock_time_to_syncd_system_time

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

 Entry    :


 Returns  :


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

LONG
set_battclock_time_to_syncd_system_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_battclock_time (&tv_battclock);
  get_system_time (&tv_tmp);
  diff = tv_diff (&tv_battclock, &tv_tmp);

  get_syncd_system_time (&tv_sysclock);
  set_battclock_time (&tv_sysclock);

  /*
     * now correct battclock time to give msecs precision
     * PROVIDED that diff was in range (<999 secs) else ignore
     * trying to be precise.
   */
  if (diff != -1)
    {
      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);
}


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


 Function :     scan_args

 Purpose:       scans and validates command line

 Entry    :


 Returns  :



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



void
scan_args (int argc, char **argv)

{
  char *ptr;
  char arch;
  char plusminus;
  int a = 0;
  int n = 0;
  int b = 0;
  int d = 0;
  int s = 0;
  int t = 0;
  int l = 0;
  int L = 0;


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

	  switch (arch)
	    {

	    case 'a':		/* adjust clock time */
	      if (a++)
		{
		  fprintf (stderr, "too many -a options\n");
		  hexit (1);
		}
	      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':
	      if (b++)
		{
		  fprintf (stderr, "too many -b options\n");
		  hexit (1);
		}
	      b_flag = TRUE;
	      break;

	    case 'd':
	      if (d++)
		{
		  fprintf (stderr, "too many -d options\n");
		  hexit (1);
		}
	      d_flag = TRUE;
	      date_ptr = ptr;
	      break;

	    case 't':
	      if (t++)
		{
		  fprintf (stderr, "too many -t options\n");
		  hexit (1);
		}
	      t_flag = TRUE;
	      time_ptr = ptr;
	      break;

	    case 's':
	      if (s++)
		{
		  fprintf (stderr, "too many -s options\n");
		  hexit (1);
		}
	      s_flag = TRUE;
	      break;


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

	    case 'l':
	      if (l++)
		{
		  fprintf (stderr, "too many -l options\n");
		  hexit (1);
		}
	      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 'L':
	      if (L++)
		{
		  fprintf (stderr, "too many -L options\n");
		  hexit (1);
		}
	      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    */
	      if (n++)
		{
		  fprintf (stderr, "too many -n options\n");
		  hexit (1);
		}
	      n_flag = TRUE;
	      xstr = ptr;
	      break;

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

	    }
	}
      else
	{
	  usage ("not an option");
	  hexit (1);
	}
    }

  if (opt_count == 0)
    return;

  /*
   * now validate options
   */

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

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

  if (d_flag || t_flag)
    {
      if (d_flag)
	{
	  if (*date_ptr == '\0')
	    {
	      fprintf (stderr, "need a date string\n");
	      hexit (1);
	    }
	}

      if (t_flag)
	{
	  if (*time_ptr == '\0')
	    {
	      fprintf (stderr, "need a time string\n");
	      hexit (1);
	    }
	}
      return;
    }

  if (l_flag && L_flag)
    {
      usage ("only one -l or -L option");	/* bad option   */
      hexit (1);
    }

  if ((opt_count > 2) && (a_flag == TRUE))
    {
      usage ("invalid options with -a option");		/* bad option   */
      hexit (1);
    }

  if ((opt_count > 2) && (n_flag == FALSE))
    {
      usage ("need a -n option");	/* bad option   */
      hexit (1);
    }
}

/***************************************************************************


 Function :     usage

 Purpose:       prints invocation parameters

 Entry    :


 Returns  :



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

void
usage (char *p)


{
  static char *rats[] =
  {
    "Usage modes :-",
    " 1) display both   : clocktool",
    " 2) set selected",
    "    from other     : clocktool -[b | s]",
    " 3) set date/time  : clocktool -[b | s] -d<date> -t<time> ",
    " 4) adjust         : clocktool -[b | s] +|-a<n>",
    " 5) log then reset : clocktool -[b | s] -l<file> [-n<str>]",
    " 6) log  only      : clocktool -[b | s] -L<file> [-n<str>]",
    "\nOptions:",
    "	-b         : battery clock",
    "	-s         : system  clock",
    "	-d<str>    : set selected clock's date to str (dd-MMM-yy)",
    "	-t<str>    : set selected clock's time to str (hh:mm[:ss])",
    "	               (can use either or both d/t options)",
    "",

    "	+|-a<n>    : change selected clock by + or - <n> millisecs",
    "",
    "	-l<file>   : log time of clock to <file> ; reset from other",
    "                     (default file =  LOG_CLOCKTOOL:)",
    "	-L<file>   : log time of clock to <file> ; don't reset from other",
    "                     (default file =  LOG_CLOCKTOOL:)",
    "	-n<str>    : append the <str> to log file entry, e.g. \"reboot\"",
    NULL
  };
  int j = 0;
  char *ptr;



  if (*p)
    fprintf (stderr, "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@werple.net.au\n\n",
	   VERSION, __DATE__, __TIME__);

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

/* */
