| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
timewarp.cpp
/*-------------------------------------------------------------------------*
TIMEWARP.CPP
Main program file for:
TimeWarp 1.1
A PC-DOS Utility that sets computer time from a master atomic
clock by using a modem to dial-up a public time service.
time warp (noun)
A hypothetical discontinuity or distortion occurring in the flow
of time that would move events from one time period to another or
suspend the passage of time.
by: Stephen R. Schmitt
*-------------------------------------------------------------------------*/
#include <assert.h>
#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <fcntl.h>
#include <io.h>
#include <math.h>
#include <process.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "dialog.h"
#include "input.h"
#include "modem.h"
// #define DEBUG_TW
#ifdef DEBUG_TW
FILE *debug;
#endif
#define MSG_ATTEMPTS 30 // attempts to get good msg
int Validate_timezone( char *, char * );
void Process_message( char * );
void To_upper( char * );
void Jul_to_greg( long n, char *, char *, int * );
int zone_offset; // time zone offset in hours
char standby[] = "Tab, Shift+Tab - select ";
char bad_tz[] = "Bad time zone ";
char modem_init[] = "Initializing modem ";
char modem_dial[] = "Dialing time source ";
char connected[] = "Connected to host system ";
char no_good[] = "No useable responses from time source ";
char no_port[] = "System does not have this comm port ";
char bad_port[] = "Bad port name ";
char not_ready[] = "The modem is not ready ";
char no_connect[] = "Could not connect to time source ";
char time_out[] = "Time out sending to comm port ";
char timed_out[] = "Timed out waiting for a response ";
char *modem_msg[7] = { "", // MODEM_OK
no_port, // NO_PORT
bad_port, // BAD_PORT
not_ready, // NOT READY
no_connect, // NO_CONNECT
time_out, // XMT_TIME_OUT
timed_out }; // RCV_TIME_OUT
char User_scrn[4000];
int User_x;
int User_y;
input Console;
modem connect;
struct
{
char *zone;
int diff;
}
TZ[] =
{
{ "UTC", 0 }, { "GMT", 0 }, // Greenwich mean time
{ "AST", -4 }, { "ADT", -3 }, // Atlantic time
{ "EST", -5 }, { "EDT", -4 }, // Eastern time
{ "CST", -6 }, { "CDT", -5 }, // Central time
{ "MST", -7 }, { "MDT", -6 }, // Mountain time
{ "PST", -8 }, { "PDT", -7 }, // Pacific time
{ "KST", -9 }, { "KDT", -8 }, // Alaskan time
{ "YST", -9 }, { "YDT", -8 }, // Yukon time
{ "HST", -10}, { "HDT", -9 }, // Hawaii-Aleutian time
{ "SST", -11}, { "SDT", -10}, // Samoa time
{ "", 0 }
};
/*-------------------------------------------------------------------------*
* "Validate_timezone" checks that timezone parameter is valid
*
* returns: 1 if valid else 0
*/
int Validate_timezone( char *zone, char *msg )
{
int i; // index
To_upper( zone ); // uppercase time zone
i = 0;
while( TZ[i].zone[0] )
{
if( !strncmp( zone, TZ[i].zone, 3 ) ) // match?
{
zone_offset = TZ[i].diff;
return( 1 );
}
else
i++;
}
sprintf( msg, bad_tz, zone );
return( 0 );
}
/*--------------------------------------------------------------------------*
* "Process_message" gets time message from the U. S. Naval Observatory
* or from National Institute of Science and Technology and
* sets CMOS clock
*
* USNO's message should be in following format:
*
* jjjjj ddd hhmmss UTC
*
* where jjjjj - Julian Day number less 2400000
* ddd - the day of the year
* hhmmss - the time
* UTC - stands for Universal Time Coordinated
*
* NIST's message should be in the following format:
*
* jjjjj yy-mm-dd hh:mm:ss tt l dut1 msadv UTC(NIST) *
*
* where yy-mm-dd - date at Greenwich
* tt - daylight savings time indicator
* l - leap second flag
* dut1 - correction for converting UTC
* msadv - one-way time delay value
*
* returns: nothing
*/
void Process_message( char *msg ) // status message
{
int i; // loop counter
int hour, min, sec; // ints for time
int yy, mm, dd; // ints for date
long julian; // julian date
struct date d; // date
struct time t; // time
time_t dtg; // date time
char buf[80]; // input buffer
for( i = 0; i < MSG_ATTEMPTS; i++ )
{
if( !connect.get_line( buf, 80 ) ) // wait for a message
{
sprintf( msg, timed_out );
return;
}
#ifdef DEBUG_TW
fprintf( debug, "message: %s:%d\n", buf, strlen( buf ) );
#endif
if( buf[5] == ' ' && // check for USNO format
buf[9] == ' ' &&
buf[16] == ' ' &&
strstr( buf, "UTC") )
{
/* parse USNO message */
sscanf( buf, "%ld %d %2d%2d%2d",
&julian,
&dd,
&hour,
&min,
&sec );
#ifdef DEBUG_TW
fprintf( debug, "USNO: %02d:%02d:%02d\n", hour, min, sec );
#endif
if( !connect.get_line( buf, 80 ) ) // wait for "mark" message
{
sprintf( msg, timed_out );
return;
}
if( buf[0] != '*' ) // time mark?
continue;
else
break;
}
else if( buf[5] == ' ' && // check for NIST format
buf[14] == ' ' &&
buf[23] == ' ' &&
strstr( buf, "NIST" ) )
{
/* parse NIST message */
sscanf( buf, "%ld %2d-%2d-%2d %2d:%2d:%2d",
&julian,
&yy,
&mm,
&dd,
&hour,
&min,
&sec );
#ifdef DEBUG_TW
fprintf( debug, "NIST: %02d:%02d:%02d\n", hour, min, sec );
#endif
break;
}
else
continue; // no .. try again
}
if( i == MSG_ATTEMPTS )
{
sprintf( msg, no_good );
return;
}
/*
* set computer clock
*/
julian += 2400000L; // prepend assumed digits
t.ti_hour = hour;
t.ti_min = min;
t.ti_sec = sec;
t.ti_hund = 0;
Jul_to_greg( julian, &d.da_mon, &d.da_day, &d.da_year );
if( t.ti_hour < abs( zone_offset ) ) // need to back up a day?
{
julian--;
Jul_to_greg( julian, &d.da_mon, &d.da_day, &d.da_year );
t.ti_hour += zone_offset + 24; // fix up hour for prev day
}
else
t.ti_hour += zone_offset;
setdate( &d );
settime( &t );
time( &dtg );
sprintf( msg, "Clock set to: %s", ctime( &dtg ) );
i = strlen( msg );
msg[i-1] = 0; // eliminate new line
}
/*-------------------------------------------------------------------------*
* "Jul_to_greg" convert from julian day number to month/day/year
*
* returns: nothing
*/
void Jul_to_greg( long n, // julian day
char *m, // month
char *d, // day
int *y ) // year with century
{
long dd; // work variable
n -= 1721118L; // base calcs on 3/1/1 BC
*y = (int)((4 * n - 1) / 146097L); // get century number
n = 4 * n - 1 - (146097L * *y); // ..remove that many days
dd = n / 4; // get to the year
n = (4 * dd + 3) / 1461; // ..within the century
*y = (int)(100 * *y + n); // ..then year with century
dd = 4 * dd + 3 - 1461 * n; // get to days within 4 yrs
dd = (dd + 4) / 4; // get days within base yr
*m = (int)(5 * dd - 3) / 153; // get month
dd = 5 * dd - 3 - (153 * *m); // get to the day ..
*d = (int)(dd + 5) / 5; // ..within the month
if( *m < 10) // need to adjust month?
*m += 3;
else
{
*m -= 9; // adjust for March base
(*y)++; // ..date and fix year too
}
return;
}
/*-------------------------------------------------------------------------*
* "To_upper" converts a null terminated string to uppercase
*
* returns: nothing
*/
void To_upper( char *s ) // string to convert
{
while( *s )
{
*s = toupper( *s );
s++;
}
}
/*------------------------------------------------------------------------*
* "Create_message_ok" displays a message box with ok button.
*
* returns: a response code
*/
int Create_message_ok( char *title, // title of message box
char *message ) // message to display
{
int rv, select;
int msg, mod, code, ampl;
int dlg_x, dlg_y, dlg_dx, dlg_dy;
int ok, ok_x, ok_y;
dialog d;
dlg_x = 24;
dlg_y = 8;
dlg_dx = 32;
dlg_dy = 6;
ok_x = 12;
ok_y = 4;
d.make_dialog( title, dlg_x, dlg_y, dlg_dx, dlg_dy );
d.make_text_ctrl( 3, 2, message );
ok = d.make_push_ctrl( ok_x, ok_y, 0, OK_BTN, " Ok " );
select = d.paint_dialog();
if( select < 0 )
{
rv = 0;
goto create_dialog_exit;
}
// process inputs
rv = 0;
while( rv != CANCEL_BTN && rv != OK_BTN )
{
msg = Console.message( &mod, &code, &l );
switch( msg )
{
case MSG_FUNCTION:
switch( code )
{
case SHIFT_TAB:
select = d.shift_tab_select( select );
break;
default:
select = d.alt_key_select( select, code );
break;
}
break;
case MSG_CHARACTER:
switch( code )
{
case ENTER:
if( ok == select )
{
rv = d.ctrl_code( select );
d.pushed_button( select );
d.select_button( select );
}
break;
case TAB:
select = d.tab_select( select );
break;
case ESCAPE:
rv = CANCEL_BTN;
break;
}
break;
case MSG_MOUSE_LF:
code++;
ampl++;
if( code >= dlg_x + ok_x &&
code <= dlg_x + ok_x + 7 &&
ampl == dlg_y + ok_y )
rv = OK_BTN;
break;
}
}
d.erase_dialog();
create_dialog_exit:
return( rv );
}
/*------------------------------------------------------------------------*
* "Get_help" spawns the help utility program and passes the help
* data file name to use and a word to lookup.
*
* returns: nothing
*/
void Get_help( char *df, // help data file
char *wd ) // word to look up
{
int ok;
Console.mouse_cursor_off();
ok = spawnlp( P_WAIT, "timehelp.exe", "timehelp.exe", df, wd, NULL );
Console.mouse_cursor_on();
if( ok == -1 )
Create_message_ok( "Error", "Missing help or data file" );
}
/*------------------------------------------------------------------------*
* "Connect_dialog" displays a preferences selection dialog box and
* processes user input.
*
* returns: a response code
*/
int Connect_dialog( char *dlg_title, // dialog title
char *telephone, // to be returned
char *time_zone, // to be returned
char *comm_port, // to be returned
char *dial_type ) // tone T or pulse P
{
int rv, select, x, y;
int msg, mod, code, ampl;
int dlg_x, dlg_y, dlg_dx, dlg_dy;
int ok, ok_x, ok_y;
int cncl, cncl_x, cncl_y;
int help, help_x, help_y;
int text, text_x, text_y;
int tone, tone_x, tone_y;
int puls, puls_x, puls_y;
int teln, teln_x, teln_y;
int zone, zone_x, zone_y;
int comm, comm_x, comm_y;
dialog d;
char status[80];
// save cursor
x = wherex();
y = wherey();
// create dialog box
dlg_x = 18;
dlg_y = 3;
dlg_dx = 45;
dlg_dy = 16;
d.make_dialog( dlg_title, dlg_x, dlg_y, dlg_dx, dlg_dy );
d.make_text_ctrl( 4, 2, "Sets the clock to official US time" );
teln_x = 4;
teln_y = 4;
teln = d.make_edit_ctrl( teln_x, teln_y, ALT_T, OK_BTN,
"#Telephone number", telephone, 20 );
tone_x = 4;
tone_y = 7;
puls_x = 4;
puls_y = 8;
if( dial_type[0] == 'P' )
{
tone = d.make_cbox_ctrl( tone_x, tone_y, ALT_N, 0,
"To#ne dialing" );
puls = d.make_cbox_ctrl( puls_x, puls_y, ALT_L, 1,
"Pu#lse dialing" );
}
else
{
tone = d.make_cbox_ctrl( tone_x, tone_y, ALT_N, 1,
"To#ne dialing" );
puls = d.make_cbox_ctrl( puls_x, puls_y, ALT_L, 0,
"Pu#lse dialing" );
dial_type[0] = 'T';
}
text_x = 4;
text_y = 11;
text = d.make_text_ctrl( text_x, text_y, standby );
comm_x = 32;
comm_y = 4;
comm = d.make_edit_ctrl( comm_x, comm_y, ALT_P, OK_BTN,
"Comm #port", comm_port, 10 );
zone_x = 32;
zone_y = 7;
zone = d.make_edit_ctrl( zone_x, zone_y, ALT_Z, OK_BTN,
"Time #zone", time_zone, 10 );
ok_x = 4;
ok_y = 14;
ok = d.make_push_ctrl( ok_x, ok_y, ALT_C, OK_BTN,
" #Connect " );
cncl_x = 20;
cncl_y = 14;
cncl = d.make_push_ctrl( cncl_x, cncl_y, 0, CANCEL_BTN,
" Quit " );
help_x = 33;
help_y = 14;
help = d.make_push_ctrl( help_x, help_y, 0, HELP_BTN,
" Help " );
select = d.paint_dialog();
d.paint_border( 3, 10, 42, 12 );
if( select < 0 )
{
rv = 0;
goto create_dialog_exit;
}
while( select != ok )
select = d.tab_select( select );
// process inputs
rv = 0;
while( rv != CANCEL_BTN )
{
msg = Console.message( &mod, &code, &l );
switch( msg )
{
case MSG_FUNCTION:
switch( code )
{
case SHIFT_TAB:
select = d.shift_tab_select( select );
break;
case HOME:
case END:
case UP_ARROW:
case LF_ARROW:
case RT_ARROW:
case DN_ARROW:
case INSERT:
case DELETE:
d.update_control( select, code );
break;
default:
select = d.alt_key_select( select, code );
break;
}
break;
case MSG_CHARACTER:
switch( code )
{
case ENTER:
rv = d.ctrl_code( select );
if( select == teln )
strcpy( telephone, d.edit_ctrl_data( select ) );
else if( select == zone )
strcpy( time_zone, d.edit_ctrl_data( select ) );
else if( select == comm )
strcpy( comm_port, d.edit_ctrl_data( select ) );
else if( select == tone )
{
dial_type[0] = 'T';
dial_type[1] = 0;
d.set_cbox( tone );
d.clear_cbox( puls );
}
else if( select == puls )
{
dial_type[0] = 'P';
dial_type[1] = 0;
d.set_cbox( puls );
d.clear_cbox( tone );
}
else if( select == ok ||
select == cncl ||
select == help )
{
d.pushed_button( select );
d.select_button( select );
}
break;
case TAB:
select = d.tab_select( select );
break;
case ESCAPE:
rv = CANCEL_BTN;
break;
default:
d.update_control( select, code );
break;
}
break;
case MSG_MOUSE_LF:
code++;
ampl++;
Console.mouse_cursor_off();
if( code >= dlg_x + ok_x &&
code <= dlg_x + ok_x + 10 &&
ampl == dlg_y + ok_y )
{
rv = OK_BTN;
while( select != ok )
select = d.tab_select( select );
d.pushed_button( select );
d.select_button( select );
}
else if( code >= dlg_x + cncl_x &&
code <= dlg_x + cncl_x + 7 &&
ampl == dlg_y + cncl_y )
{
rv = CANCEL_BTN;
while( select != cncl )
select = d.tab_select( select );
d.pushed_button( select );
d.select_button( select );
}
else if( code >= dlg_x + help_x &&
code <= dlg_x + help_x + 7 &&
ampl == dlg_y + help_y )
{
rv = HELP_BTN;
while( select != help )
select = d.tab_select( select );
d.pushed_button( select );
d.select_button( select );
}
else if( code >= dlg_x + teln_x &&
code <= dlg_x + teln_x + 19 &&
ampl == dlg_y + teln_y + 1 )
{
rv = 0;
while( select != teln )
select = d.tab_select( select );
}
else if( code >= dlg_x + zone_x &&
code <= dlg_x + zone_x + 9 &&
ampl == dlg_y + zone_y + 1 )
{
rv = 0;
while( select != zone )
select = d.tab_select( select );
}
else if( code >= dlg_x + comm_x &&
code <= dlg_x + comm_x + 9 &&
ampl == dlg_y + comm_y + 1 )
{
rv = 0;
while( select != comm )
select = d.tab_select( select );
}
else if( code >= dlg_x + tone_x &&
code <= dlg_x + tone_x + 16 &&
ampl == dlg_y + tone_y )
{
rv = 0;
while( select != tone )
select = d.tab_select( select );
}
else if( code >= dlg_x + puls_x &&
code <= dlg_x + puls_x + 16 &&
ampl == dlg_y + puls_y )
{
rv = 0;
while( select != puls )
select = d.tab_select( select );
}
Console.mouse_cursor_on();
break;
}
if( rv == HELP_BTN )
{
rv = 0;
Get_help( "tw.dat", "index" );
}
if( rv == OK_BTN )
{
rv = 0;
if( Validate_timezone( time_zone, status ) )
{
if( connect.validate_port( comm_port ) )
{
d.update_text( text, modem_init );
Console.mouse_cursor_off();
if( connect.set_up_comm() )
{
d.update_text( text, modem_dial );
if( connect.place_call( telephone, dial_type[0] ) )
{
d.update_text( text, connected );
Process_message( status );
delay( 1000 );
}
connect.hang_up();
}
else
Console.mouse_reset();
Console.mouse_cursor_on();
}
int sc = connect.status_code();
if( sc )
strcpy( status, modem_msg[sc] );
}
d.update_text( text, status );
}
}
d.erase_dialog();
create_dialog_exit:
gotoxy( x, y );
return( rv );
}
/*-------------------------------------------------------------------------*
* "Save_user_screen" saves the user's screen and directory.
*
* returns: nothing
*/
void Save_user_screen()
{
struct text_info ti;
gettextinfo( &ti );
if( ti.currmode != C80 )
textmode( C80 );
gettext( 1, 1, 80, 25, User_scrn );
User_x = wherex();
User_y = wherey();
_setcursortype( _NORMALCURSOR );
}
/*-------------------------------------------------------------------------*
* "Restore_user_screen" restores the user's screen and directory.
*
* returns: nothing
*/
void Restore_user_screen()
{
puttext( 1, 1, 80, 25, User_scrn );
gotoxy( User_x, User_y );
_setcursortype( _NORMALCURSOR );
}
/*-------------------------------------------------------------------------*
* "read_ini_file" read the initialization file.
*
* returns: nothing
*/
void read_ini_file( char *tel, // telephone number
char *tz, // time zone
char *cp, // comm port
char *pt ) // dial type
{
FILE *fp = fopen( "timewarp.ini", "r" );
if( !fp )
{
// set default initialization data
strcpy( tel, "1-202-653-0351" );
strcpy( tz, "EST" );
strcpy( cp, "COM1" );
pt[0] = 'T';
pt[1] = 0;
}
else
{
// read the initialization data
fscanf( fp, "%s %s %s %s", tel, tz, cp, pt );
pt[1] = 0;
fclose( fp );
}
}
/*-------------------------------------------------------------------------*
* "save_ini_file" saves the dial-up settings to a disk file.
*
* returns: nothing
*/
void save_ini_file( char *tel, // telephone number
char *tz, // time zone
char *cp, // comm port
char *pt ) // dial type
{
FILE *fp = fopen( "timewarp.ini", "w" );
if( !fp )
return;
// save the data as a string
pt[1] = 0;
fprintf( fp, "%s %s %s %s", tel, tz, cp, pt );
fclose( fp );
}
void main()
{
char tel[32];
char tz[32];
char cp[32];
char pt[32];
read_ini_file( tel, tz, cp, pt );
Save_user_screen();
Console.set_mouse( 0, 0 );
Console.mouse_cursor_on();
#ifdef DEBUG_TW
debug = fopen( "timewarp.msg", "w" );
#endif
Connect_dialog( "TimeWarp 1.1", tel, tz, cp, pt );
#ifdef DEBUG_TW
fclose( debug );
#endif
save_ini_file( tel, tz, cp, pt );
Console.mouse_cursor_off();
Restore_user_screen();
}
| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
Copyright © 2004, Stephen R. Schmitt