| 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, &ampl );
        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, &ampl );
        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