#pragma     extend

#define     RMXRET  int
#define     RMXOBJ  int
#define     FALSE   0
#define     TRUE    ~FALSE

#include    <rmx.h>
#include    <stdio.h>
#include    <string.h>
#include    <stdlib.h>
#include    <stddef.h>
#include    <time.h>

char        command[256], excp_info[5], buff[50], *this, *next;
int         ci_tkn, co_tkn, cmd_tkn, status, cmd_status, index, time_param,
            date_specified;
U16         year, month, day, hour, minute, second, rmx_timeout, days, years;
U32         start, end, timeout;
DATE_TIME   dt;

/****************************************************************************/
/* MDAYS - Is an array containing counts of the number of days prior to     */
/* each month.                                                              */
/****************************************************************************/

static const U16 mdays[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

/****************************************************************************/
/* MLENS - Is an array containing counts of the number of days in month     */
/****************************************************************************/

static const U16 mlens[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

/****************************************************************************/
/* explain_ccode_and_die - outputs explanation of erroneous condition code  */
/* and aborts execution                                                     */
/****************************************************************************/

const char whilel[]  = "while loading command";
const char whilee[]  = "while executing command";

void explain_ccode_and_die( int ccode, const char *msg )
    {
    buff[0] = 0;
    RQCFORMATEXCEPTION( buff, 50, ccode, 1, &status );

    if ( status != E_OK )
        sprintf( buff, "%04X: E$UNKNOWN$EXCEPTION", ccode );
    else
        (void)cstr( buff, buff );

    printf( "\n%s, %s\n", buff, msg );
    exit( ccode );
    }

/****************************************************************************/
/* execcmd - execute a single command                                       */
/****************************************************************************/

void execcmd( char *cmd )
    {
    udistr( cmd, cmd );    
    RQCSENDCOMMAND( cmd_tkn, cmd, &cmd_status, &status );

    if ( ( status != E_OK ) && ( status != E_CONTINUED ) )
        explain_ccode_and_die( status, whilel );
    
    if ( cmd_status != E_OK )
        explain_ccode_and_die( cmd_status, whilee );
    }
                    
/****************************************************************************/
/* execcmds - executes command(s)                                           */
/****************************************************************************/

void execcmds( char *cmd )
    {
    this = cmd;

    while ( ( next = strchr( this, '!' ) ) != NULL )
        {
        *next++ = '\0';
        execcmd( this );
        this = next;
        }
    
    execcmd( this );
    }

/****************************************************************************/
/* exit_param() - kills the program with explanation about bad parameter(s) */
/****************************************************************************/

void exit_param( char *reason )
    {
    putchar( '\n' );
    puts( reason );
    puts( "\nUSE: \"at [dd/mm/yy] hh:mm:ss command [! command]*\"\n" );
    exit( E_PARAM );
    }
        
/****************************************************************************/
/* main                                                                     */
/****************************************************************************/

int main( int argc, char *argv[] )
    {
    if ( argc < 3 )
        exit_param( "Insufficient parameters !!!" );

    if ( ( argv[1][1] == '/' ) || ( argv[1][2] == '/' ) )
        {
        date_specified = TRUE;
        time_param = 2;

        year = month = day = 0;
        index = sscanf( argv[1], "%u/%u/%u", &day, &month, &year );

        if ( year < 78 )
            year = year + 2000;
        else
            year = year + 1900;

        if (    ( index != 3 )
             || ( month > 12 ) || ( month == 0 )
             || ( day > mlens[month] ) || ( day == 0 ) )
            exit_param( "Invalid date specification !!!" );

        if ( ( month == 2 ) && ( day == 29 ) && ( year & 3 ) )
            exit_param( "Invalid date specification !!!" );
        }
    else
        {
        date_specified = FALSE;
        time_param = 1;
        }
    
    hour = minute = second = 0;
    index = sscanf( argv[time_param], "%u:%u:%u", &hour, &minute, &second );

    if ( ( index < 1 ) || ( index > 3 ) || ( hour > 23 ) || ( minute > 59 ) || ( second > 59 ) )
        exit_param( "Invalid time specification !!!" );

    excp_info[4] = NO_EXCEPTIONS;
    RQSETEXCEPTIONHANDLER( excp_info, &status );
    
    ci_tkn = RQCGETINPUTCONNECTION( CONSOLE_INPUT_DEVICE, &status );

    if ( status == E_OK )
        co_tkn = RQCGETOUTPUTCONNECTION( CONSOLE_OUTPUT_DEVICE, OVER_PREPOSITION, &status );
    
    if ( status == E_OK )
        cmd_tkn = RQCCREATECOMMANDCONNECTION( ci_tkn, co_tkn, 0, &status );
    
    if ( status != E_OK )
        explain_ccode_and_die( status, whilee );

    if ( date_specified )
        {
        /* calculate days from beginning of 1978 til date-time specified */

        years = year - 1980;                    /* # years since 1980       */
                                                /* (which is first leap     */
                                                /* year after 1978).        */

        days =   ((years + 2) * 365)            /* calendar days in         */
                                                /* previous years           */
               + ((years + 3) >> 2)             /* leap days in previous    */
                                                /* years                    */
               + mdays[month - 1]               /* calendar days in         */
                                                /* previous months          */
               + (day - 1);                     /* previous days in this    */
                                                /* month                    */

        if ( ( month > 2 ) && !(years & 3) )    /* add leap day for this    */
            ++days;                             /* year if has occurred     */

        /* calculate seconds from beginning of 1978 til date-time specified */

        end = ((U32)days * 86400L) + ((U32)hour * 3600L) + ((U32)minute * 60L) + (U32)second;

        /* verify the this date-time is not in the past */
        
        start = RQGETTIME( &status );
        
        if ( start > end )
            exit_param( "Date-time has already passed !!!" );

        /* calculate seconds from now til date-time specified */

        timeout = end - start;
        }
    else
        {
        end = ((U32)hour * 3600L) + ((U32)minute * 60L) + (U32)second;
        start = RQGETTIME( &status ) % 86400L;

        if ( start > end )
            /* this time does not occur til tomorrow */
            timeout = 86400L + end - start;
        else
            /* this time occurs later today */
            timeout = end - start;
        }

    while ( timeout )
        {
        if ( timeout >= 655 )
            {
            rmx_timeout = 65500;
            timeout -= 655;
            }
        else
            {
            rmx_timeout = timeout * 100;
            timeout = 0;
            }

        RQSLEEP( rmx_timeout, &status );
        }

    strcpy( command, argv[time_param + 1] );

    for ( index = time_param + 2; index < argc; index++ )
        {
        strcat( command, " " );
        strcat( command, argv[index] );
        }

    execcmds( command );
    exit( 0 );
    }
