home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / nistime2.zip / nistime.c < prev    next >
C/C++ Source or Header  |  2001-11-02  |  22KB  |  554 lines

  1. /* nistime.c - query and set time from NIST server */
  2.  
  3. #define VERSION_STRING "Version 0.2d   02-nov-2001"
  4.  
  5. /* ------------------------------ */
  6. /* Feature test macros            */
  7. /* ------------------------------ */
  8. /* #define _POSIX_SOURCE                  /* Always require POSIX standard */
  9.  
  10. /* ------------------------------ */
  11. /* Standard include files         */
  12. /* ------------------------------ */
  13.  
  14. #define INCL_DOSDATETIME
  15. #define INCL_DOSPROCESS
  16. #include <os2.h>
  17.  
  18. #include <stdio.h>
  19. #include <time.h>
  20. #include <string.h>
  21. #include <ctype.h>
  22. #include <stdlib.h>
  23. #include <math.h>
  24.  
  25. #include <types.h>                     /* Will be OS/2 TCP/IP types.h file */
  26. #include <sys/socket.h>                /* More TCP/IP include files        */
  27. #include <netinet/in.h>
  28. /* #include <arpa/inet.h> */
  29. #include <netdb.h>                     /* struct hostent */
  30.  
  31. /* ------------------------------ */
  32. /* Local include files            */
  33. /* ------------------------------ */
  34.  
  35. /* ------------------------------- */
  36. /* My local typedef's and defines  */
  37. /* ------------------------------- */
  38.  
  39. #define  panic    SysPanic(__FILE__, __LINE__)
  40.  
  41. #define NIST_TIME_HOST           "time-a.nist.gov"
  42. #define NIST_TIME_PORT           13             /* specific port */
  43. #define UNIX_EPOCH_MJD           40587          /* UNIX epoch date */
  44.  
  45. #define NIST_PKT_DELAY            50.0          /* network delay, msec */
  46. #define DELAY_MAX_DFLT           250.0          /* max. network delay, msec */
  47. #define DIFF_MIN                 0.550          /* adjustment threshold, sec */
  48. #define BUF_SIZE                 2000           /* message buffer size*/
  49.  
  50. #define notdiffer() (fabs( diff) < DIFF_MIN)
  51.  
  52. #define HEALTH_OK       0
  53. #define MSG_TERSE       0
  54. #define MSG_SHORT       1
  55. #define MSG_VERBOSE     2
  56. #define MSG_INVALID     3
  57. #define SET_NEVER       0
  58. #define SET_HEALTHY     1
  59. #define SET_ALWAYS      2
  60. #define SET_ASK_USER    3
  61. #define SET_INVALID     4
  62.  
  63. /* successful returns */
  64. #define EXIT_OK                             0       /* clock not adjusted */
  65. #define EXIT_ADJUSTED                       1       /* clock was adjusted */
  66.  
  67. /* non-recoverable errors */
  68. #define ERROR_INVALID_ARGUMENT            100
  69. #define ERROR_CANNOT_SET_CLOCK            101
  70. #define ERROR_MEMORY_ALLOCATION           110
  71. #define ERROR_SOCKET_CREATION             111
  72.  
  73. /* possibly recoverable with a different server name/address */
  74. #define ERROR_INTERNET_ADDRESS            200
  75. #define ERROR_HOST_NAME_RESOLUTION        201
  76. #define ERROR_FAILED_TO_CONNECT           210
  77. #define ERROR_NOTHING_RECEIVED            211
  78. #define ERROR_SERVER_NOT_HEALTHY          212
  79. #define ERROR_MESSAGE_FORMAT              213
  80.  
  81. /* possibly recoverable by retrying with current server */
  82. #define ERROR_NETWORK_DELAY               300
  83.  
  84. /* ------------------------------- */
  85. /* My external function prototypes */
  86. /* ------------------------------- */
  87.  
  88. /* ------------------------------- */
  89. /* My internal function prototypes */
  90. /* ------------------------------- */
  91.  
  92. static int sw(int argc, char *arg, char *let, long *val);
  93. static int ShiftTime( double diff);
  94. double MakeTime( DATETIME *pt);
  95. void OneShot( ULONG msec);
  96. static void PrintUsage(void);
  97.  
  98. /* ------------------------------- */
  99. /* My usage of other external fncs */
  100. /* ------------------------------- */
  101.  
  102. /* ------------------------------- */
  103. /* Locally defined global vars     */
  104. /* ------------------------------- */
  105.  
  106. /* ---------------------------------------------------------------------------
  107. - client process to connect to the daytime service via port 13 from nist
  108. - time server.
  109. -
  110. - This OS/2 implementation is a port of the unix utility by the same name
  111. - obtained by anonymous FTP from time_a.timefreq.bldrdoc.gov.  My apologies
  112. - to Judah Levine for the hack of his code.  For questions about this OS/2
  113. - implementation, contact:
  114. -
  115. -   Michael Thompson
  116. -   Cornell University
  117. -   Dept. of Materials Science
  118. -   129 Bard Hall
  119. -   tommy@msc.cornell.edu
  120. -
  121. - Further modified 31-oct-2001 to add a few useful features, by:
  122. -   Pieter Bras
  123. -   Cambridge, MA
  124. -   pbras@pobox.com
  125. -
  126. - The client process uses the time message received to check (and optionally 
  127. - to set) the time of the local clock.  the comparison assumes that the local
  128. - clock keeps time in seconds from 1/1/70 which is the UNIX standard, and it
  129. - computes the time difference by converting the received MJD to seconds
  130. - since 1/1/70 and adding the received hr, min and sec, also converted to
  131. - seconds.  If the local machine keeps time in some other way, then the
  132. - comparison method will have to change, but the rest should be okay.
  133. -
  134. - This software was developed with US Government support and it may not be
  135. - sold, restricted or licensed.  You may duplicate this program provided
  136. - that this notice remains in all of the copies, and you may give it to
  137. - others provided they understand and agree to this condition.
  138. -
  139. - This program and the time protocol it uses are under development and the
  140. - implementation may change without notice.
  141. -
  142. - For questions or additional information, contact:
  143. -
  144. -   Judah Levine
  145. -   Time and Frequency Division
  146. -   NIST/847
  147. -   325 Broadway
  148. -   Boulder, Colorado 80303
  149. -   (303) 492 7785
  150. -   jlevine@time_a.timefreq.bldrdoc.gov
  151. --------------------------------------------------------------------------- */
  152. int main( int argc, char *argv[]) {
  153.  
  154. /* Internet socket access parameters */
  155.    char *cp;                           /* server name or addr in . notation */
  156.    int  pserv = NIST_TIME_PORT;        /* port for time service         */
  157.    struct sockaddr_in *sin;            /* socket address structure      */
  158.    int s;                              /* socket number                 */
  159.    int length;                         /* size of message               */
  160.    char *buf;                          /* holds message                 */
  161.  
  162.    struct in_addr address;             /* ip address */
  163.    struct hostent *hp;                 /* host data */
  164.  
  165.    DATETIME dt1, dt2;
  166.    time_t nist_time;                   /* Time since epoch */
  167.    double msadj;                       /* msec adjustment to nist_time */
  168.  
  169.    double my_ltime, nist_ltime;        /* local time, seconds */
  170.    double diff;                        /* time difference, local - NIST */
  171.    double delay;                       /* network delay, msec */
  172.  
  173. /* Values obtained from the internet message */
  174.    long mjd0  = UNIX_EPOCH_MJD;        /* MJD of the UNIX EPOCH         */
  175.    long mjd;                           /* holds parsed received mjd     */
  176.    int yr,mo,dy,hr,min,sec;            /* holds parsed received time    */
  177.    float ms_adv;
  178.    int day_light,                      /* Daylight savings flag         */
  179.        leap_sec,                       /* Leap second status            */
  180.        health;                         /* Health of server status       */
  181.  
  182. /* Local variables */
  183.    char achr;                          /* Random character              */
  184.    char let;                           /* command-line letter           */
  185.    long val;                           /* command line value            */
  186.  
  187. /* ---------------------------------------------------------------------------
  188. -  the following variables define what this program should do and what
  189. -  output should be produced.  The values below are the defaults which
  190. -  may be changed by characters on the command line as described below.
  191. -  The effect of each variable is as follows:
  192. -
  193. - Command-Line Switch   effect
  194. -
  195. -  -dnnn    Fail if round-trip network delay exceeds nnn milliseconds.
  196. -
  197. -  -m0      msg  = 0 Do not produce any messages; only time difference is 
  198. -                    written to standard output.
  199. -  -m1 or -M     = 1 Produce short messages.
  200. -  -m2           = 2 Produce longer messages.
  201. -
  202. -  -s0      set  = 0 Do not adjust local time.
  203. -  -s1 or -S     = 1 Adjust local time if server is healthy.
  204. -  -s2           = 2 Adjust local time even if server is unhealthy.
  205. -  -s3           = 3 Ask operator first.
  206. -
  207. --------------------------------------------------------------------------- */
  208. int msg = MSG_SHORT;                   /* default: short messages */
  209. int set = SET_ASK_USER;                /* default: ask before setting clock */
  210. double delay_max = DELAY_MAX_DFLT;     /* default network delay, msec */
  211.  
  212. /* parse command line switches */
  213.    ++argv; --argc;                                    /* Skip command name */
  214.    while ( sw( argc, argv[0], &let, &val) != 0) {     /* Switch present */
  215.       switch( let) {
  216.          case '?':
  217.          case 'h':
  218.          case 'H':
  219.             PrintUsage();
  220.             return( EXIT_OK);
  221. /* message options */
  222.          case 'M':
  223.             val = MSG_SHORT;
  224.          case 'm':
  225.             if( (val >= 0) && (val < MSG_INVALID) )
  226.                msg=val;
  227.             break;
  228. /* clock-setting options */
  229.          case 'S':
  230.             val= SET_HEALTHY;
  231.          case 's':
  232.             if( (val >= 0) && (val < SET_INVALID) )
  233.                set=val;
  234.             break;
  235. /* max. round-trip network delay (msec) */
  236.          case 'd':
  237.             if( val > 0)
  238.                delay_max = val;
  239.             break;
  240.  
  241.          default:
  242.             fprintf( stderr, "\nSwitch %c not recognized.\n", let);
  243.             return( ERROR_INVALID_ARGUMENT);
  244.             break;
  245.       }
  246.       argc--;  /* decrement argument counter */
  247.       argv++;  /* and increment pointer      */
  248.    }
  249.  
  250. /* anything left on the command line assumed to be a time server name/addr */
  251.    cp = argc > 0? argv[0]: NIST_TIME_HOST;         /* server name or addr */
  252.  
  253. /* -----------------------------------------------------------------
  254. -- (1) Make sure we are using the TZ variable
  255. -- (2) Convert xxx.xxx internet address to internal format
  256. -- (3) allocate space for socket info, and fill int
  257. -- (4) Create socket
  258. -- (5) Connect to server.  Note this is a stream server and
  259. --     record boundaries are not preserved.
  260. -- (6) Query local time via time() call
  261. -- (7) Close socket and free allocated memory
  262. ----------------------------------------------------------------- */
  263.    _tzset();                                       /* Get timezone from TZ */
  264.    sock_init();                                    /* Initialize sockets */
  265.  
  266. /* allocate a big message buffer, in case we get an oversize message */
  267.    if( (buf = calloc( BUF_SIZE, 1)) == NULL) {
  268.       fprintf( stderr, "Unable to allocate memory for message buffer.\n");
  269.       return( ERROR_MEMORY_ALLOCATION);
  270.       }
  271.  
  272. /* if addr is all dotted-decimal chars, use that as IP addr */
  273.    if( strspn( cp, "0123456789.") == strlen( cp)) {
  274.       if( (address.s_addr = inet_addr( cp)) == -1) {  /* Internal format */
  275.          fprintf( stderr, "Internet address error: %s\n", cp);
  276.          return( ERROR_INTERNET_ADDRESS);
  277.          }
  278.       }
  279. /* if addr is not all dotted-decimal chars, try to resolve name to IP addr */
  280.    else {
  281.       hp = gethostbyname( cp);
  282.       if( hp)
  283.          address.s_addr = *( (long int *) hp->h_addr);
  284.       else {
  285.          fprintf( stderr, "Can't resolve host name: %s\n", cp);
  286.          return( ERROR_HOST_NAME_RESOLUTION);
  287.          }
  288.       }
  289.    
  290.    if( msg == MSG_VERBOSE) {
  291.       cp = inet_ntoa( address);
  292.       printf( "Trying time server: %s", cp == (char *) -1? "unknown": cp);
  293.       hp = gethostbyaddr( (char *) &address.s_addr, sizeof( address.s_addr),
  294.          AF_INET);
  295.       printf( "  hostname: %s\n", hp? hp->h_name: "(unable to resolve)");
  296.       }
  297.  
  298.    if( (sin = calloc( sizeof( *sin), 1)) == NULL) {   /* Allocate space  */
  299.       fprintf( stderr, "Unable to allocate memory for socket info.\n");
  300.       return( ERROR_MEMORY_ALLOCATION);
  301.       }
  302.    sin->sin_family      = AF_INET;                    /* Fill in request */
  303.    sin->sin_addr.s_addr = address.s_addr;
  304.    sin->sin_port        = htons( pserv);
  305.  
  306. /* Now, create socket, open and read */
  307.    if( (s = socket( AF_INET, SOCK_STREAM, 0)) < 0) {  /* Get a TCP socket */
  308.       psock_errno( "Socket creation error");
  309.       return( ERROR_SOCKET_CREATION);
  310.       }
  311.    DosGetDateTime( &dt1);
  312.    if( connect( s, (struct sockaddr *) sin, sizeof( *sin) ) < 0) {
  313.       psock_errno( "Failed to connect to NIST server");
  314.       soclose( s);
  315.       return( ERROR_FAILED_TO_CONNECT);
  316.       }
  317.    if( (length = recv( s, buf, BUF_SIZE-1, 0)) <= 0) {
  318.       psock_errno( "Nothing received from NIST server");
  319.       soclose( s);
  320.       return( ERROR_NOTHING_RECEIVED);
  321.       }
  322.    buf[length] = '\0';                                /* terminate msg  */
  323.  
  324. /* Immediately, get local time as well for comparison */
  325.    DosGetDateTime( &dt2);
  326.    my_ltime = MakeTime( &dt2);
  327.    delay = (my_ltime - MakeTime( &dt1))*1000.;        /* round-trip msec */
  328.    if( msg == MSG_VERBOSE)
  329.        printf( "round-trip delay = %d msec.\n", (int) (delay+0.5));
  330.    if( delay > delay_max) {
  331.       fprintf( stderr, "Network delay too long.\n");
  332.       return( ERROR_NETWORK_DELAY);
  333.       }
  334.    
  335. /* And close socket, free allocated space */
  336.    soclose( s);                        /* Close port and free memory now */
  337.    free( sin);
  338.  
  339. /* --------------------------------------------------------------------------
  340. -- Make sure the message is in NIST format.
  341. -- Convert received time to seconds since EPOCH.
  342. -- The seconds value gives the time to the next full second. The msec error
  343. -- is given by msADV, so it must be subtracted from NIST seconds. Note that
  344. -- NIST reduces the actual error value by 50 msec to allow for network delays.
  345. --------------------------------------------------------------------------- */
  346.    if( strstr( buf, "UTC(NIST" ) == 0) {
  347.       fprintf( stderr, "Received message not in NIST format.\n");
  348.       return( ERROR_MESSAGE_FORMAT);
  349.       }
  350.    sscanf(buf," %ld %2d-%2d-%2d %2d:%2d:%2d %d %d %d %f", &mjd, &yr,
  351.       &mo, &dy, &hr, &min, &sec, &day_light, &leap_sec, &health, &ms_adv);
  352.    nist_time = 86400*(mjd-mjd0) + 3600*hr + 60*min + sec;
  353.  
  354. /* Adjust msADV for measured network delay, and apply to NIST seconds */
  355.    msadj = ms_adv + NIST_PKT_DELAY - 0.35*delay;
  356.    nist_ltime = mktime( localtime( &nist_time)) - msadj/1000.;
  357.  
  358. /* calculate offset (seconds): (system time) - (NIST time) */
  359.    diff = my_ltime - nist_ltime;
  360.  
  361. /* --------------------------------------------------------------------------
  362. -- Output various reports to stdout depending on the setting of msg parameter.
  363. --------------------------------------------------------------------------- */
  364. /* Output desired messages */
  365.    if( msg != MSG_TERSE) {
  366.       printf( " Time message received:\n");
  367.       printf( "                        D  L\n");
  368.       printf( " MJD  YY MM DD HH MM SS ST S H  Adv.%s", buf);
  369.       }
  370.  
  371.    if( msg == MSG_VERBOSE) {                 /* longer messages selected */
  372.       if( (day_light == 0) || (day_light > 51) ) {
  373.          printf( "Standard Time now in effect\n");
  374.          if( day_light > 51) 
  375.            printf( "Change to Daylight Saving Time in %d days\n", day_light-51);
  376.          }
  377.       if( (day_light <= 50) && (day_light > 1) ) {
  378.          printf( "Daylight Saving Time now in effect\n");
  379.          if( (day_light > 1) && (day_light != 50) )
  380.             printf( "Change to Standard Time in %d days\n", day_light-1);
  381.          }
  382.       if( day_light == 1)  printf("Standard time begins at 2am today\n");
  383.       if( day_light == 51) printf("Daylight Saving Time begins at 2am today\n");
  384.       if( leap_sec == 1)   printf("Leap Second to be added at end of month\n");
  385.       if( leap_sec == 2)   printf("Second to be dropped at end of month\n");
  386.       }
  387.  
  388.    cp = (msg == MSG_TERSE)? "%.2f\n": "Local Clock - NIST = %.2f second(s)\n";
  389.    printf( cp, diff);
  390.  
  391. /* --------------------------------------------------------------------------
  392. -- Deal with possibly resetting the clock based on setting of set parameter.
  393. --------------------------------------------------------------------------- */
  394.    if( (set == SET_NEVER) || notdiffer() ) {
  395.       if( notdiffer() && (msg != MSG_TERSE) )
  396.          printf("\n %s\n", health == HEALTH_OK? "No adjustment is needed.":
  397.             "No adjustment; server is not healthy.");
  398.       return( health == HEALTH_OK? EXIT_OK: ERROR_SERVER_NOT_HEALTHY);
  399.       }
  400.  
  401.    if( (health != HEALTH_OK) && (msg != MSG_TERSE) )
  402.       printf( "\n Server is not healthy, adjustment not recommended.");
  403.  
  404.    achr = 'n';                         /* default is not to set */
  405.    if( set == SET_ASK_USER) {          /* action depends on answer to query */
  406.       printf( "Do you want to adjust the local clock ? [y/n] ");
  407.       fflush( stdout);
  408.       achr = getchar();
  409.       }
  410.  
  411.    if( (set == SET_ALWAYS)
  412.     || ( (set == SET_HEALTHY) && (health == HEALTH_OK) )
  413.     || (tolower( achr) == 'y') ) {
  414.       if( ShiftTime( diff) != 0) {
  415.          fprintf( stderr, "Unable to modify system clock\n");
  416.          return( ERROR_CANNOT_SET_CLOCK);
  417.          }
  418.       if( msg != MSG_TERSE)
  419.          printf( "Local clock time reset to NIST standard\n");
  420.       return( EXIT_ADJUSTED);
  421.       }
  422.  
  423.    return( health == HEALTH_OK? EXIT_OK: ERROR_SERVER_NOT_HEALTHY);
  424.    }
  425.  
  426. /* ---------------------------------------------------------------------------
  427. -- Inputs:  diff - Time difference from current setting 
  428. --------------------------------------------------------------------------- */
  429. static int ShiftTime( double diff)
  430.    {
  431.    DATETIME os2_time;
  432.    struct tm *tm;
  433.    time_t new_time;
  434.    double ltime, lsec;
  435.    int rc = 0;
  436.  
  437. /* wait for a safe moment to set the clock */
  438.    while( 1) {
  439.       DosGetDateTime( &os2_time);
  440.       if( os2_time.hundredths < 40)
  441.          break;
  442.       OneShot( 10*( 100 - os2_time.hundredths));
  443.       }
  444.  
  445.    os2_time.hundredths = 0;
  446.    ltime = MakeTime( &os2_time) - diff;
  447.    lsec = floor( ltime + 0.5);                     /* round to nearest sec */
  448.    new_time = (time_t) lsec;
  449.    tm = localtime( &new_time);                     /* Fill in the structure */
  450.    os2_time.hours      = tm->tm_hour;
  451.    os2_time.minutes    = tm->tm_min;
  452.    os2_time.seconds    = tm->tm_sec;
  453.    os2_time.day        = tm->tm_mday;
  454.    os2_time.month      = tm->tm_mon  + 1;
  455.    os2_time.year       = tm->tm_year + 1900;
  456.    os2_time.weekday    = tm->tm_wday;
  457.  
  458.    rc = DosSetDateTime( &os2_time);
  459.    if( rc)
  460.       fprintf( stderr, "error from DosSetDateTime: rc = %d\n", rc);
  461.    return( rc);
  462.    }
  463.  
  464. /* convert OS/2 DATETIME values to a (double) Unix time */
  465. double MakeTime( DATETIME *pt)
  466.    {
  467.    struct tm t;
  468.    double d;
  469.  
  470.    t.tm_year = pt->year - 1900;
  471.    t.tm_mon  = pt->month - 1;
  472.    t.tm_mday = pt->day;
  473.    t.tm_hour = pt->hours;
  474.    t.tm_min  = pt->minutes;
  475.    t.tm_sec  = pt->seconds;
  476.    d = (double) mktime( &t) + (pt->hundredths)/100.;
  477.    return( d);
  478.    }
  479.  
  480. /* delay for some msec value */
  481. void OneShot( ULONG msec)
  482.    {
  483.    DosSleep( msec);              /* probably good enough */
  484.    }
  485.  
  486. /* ---------------------------------------------------------------------------
  487. -  this subroutine parses switches on the command line. 
  488. -  switches are of the form -<letter><value>.  If one is found, a pointer to
  489. -  the letter is returned in let and a pointer to the value in val as a 
  490. -  long integer.
  491. -
  492. -  parameters argc and argv are passed in from the main calling program.
  493. -  Note that if argc = 0, no arguments are left.
  494. -  if a switch is decoded, the value of the function is 1, otherwise zero.
  495. -  
  496. -  a number following the letter is decoded as a decimal value unless it
  497. -  has a leading x in which case it is decoded as hexadecimal.
  498. --------------------------------------------------------------------------- */
  499. static int sw( int argc, char *arg, char *let, long *val)
  500.    {
  501. /* either nothing is left or what is left is not a switch */
  502.    if( (argc == 0) || (*arg != '-') ) {
  503.       *let = '\0';
  504.       *val = 0;
  505.       return( 0);
  506.       }
  507.  
  508.    ++arg;                        /* Jump over the - sign */
  509.    *let = *arg++;                /* Letter option        */
  510.    if( *arg != 'x') {            /* if next char is not x, decode number */
  511.       *val = atol(arg);
  512.       }
  513.    else {                        /* Use sscanf to interpret complex value */
  514.       sscanf( arg+1, "%lx", val);
  515.       }
  516.    return( 1);
  517.    }
  518.  
  519. /* ---------------------------------------------------------------------------
  520. - Routine to print the usage in response to a -? option.
  521. --------------------------------------------------------------------------- */
  522. static void PrintUsage( void) {
  523.  
  524.    fputs(
  525. "NISTIME for OS/2 Warp   " VERSION_STRING "\n"
  526. "\n"
  527. "Syntax: nistime [ options ]  [ server ]\n"
  528. "\n"
  529. "  Connects to the daytime service on NIST time server " NIST_TIME_HOST "\n"
  530. "  using tcp/ip port 13. The server returns the current time which is compared\n"
  531. "  with the local clock. The difference may be used to adjust the local clock.\n"
  532. "  The optional 'server' argument specifies an alternate NIST time server;\n"
  533. "  it may be entered as host name or dotted-decimal IP address.\n"
  534. "\n"
  535. "Options:\n"
  536. "  -dnnn  Fail if round-trip network delay exceeds nnn msec. (default: 250)\n"
  537. "  -m0  Terse mode.  Only the time difference in seconds is written\n"
  538. "       to standard output.  A positive value means local clock is fast.\n"
  539. "  -m1  Display short time messages. (also -M and default)\n"
  540. "  -m2  Display verbose time messages.\n"
  541. "  -s0  Do not set the local clock.\n"
  542. "  -s1  Set the local clock if the server is healthy. (also -S)\n"
  543. "  -s2  Set the local clock even if the server is not healthy.\n"
  544. "  -s3  Query operator before setting the clock. (default)\n"
  545. "Bugs:\n"
  546. "  (1) Time is set to the nearest second only (+/- 0.5-second error).\n"
  547. "  (2) Time is instantaneously reset and thus may be non-monotonic.\n"
  548.    , stdout);
  549.  
  550.    return;
  551.    }
  552.  
  553. /* <eof> */
  554.