/*

                                  DateChk
                         written by Paul Mclachlan

*/

/*
 *  Checks the current date against the datestamp for this program
 *  and checks that time is moving in the correct direction
 *  Sets the datestamp of the program again if time is, otherwise
 *  exits with a warn.
 *
 *  Allowing a:
 *      if WARN
 *          sys:prefs/time
 *      endif
 *  from a script file or whatever
 *
 *  The only weakness with this method is that the program MUST be run
 *  using its full path.  So C:DateChk is OK, which just DateChk is not,
 *  unless DateChk is in the Current Directory.  Why?  The program can
 *  only check what the user entered to call the program, not where it
 *  actually is.  Under 2.x+ this can be circumvented through the PROGDIR:
 *  assign, but this is not a 2.x+ only program, and we cannot rely on
 *  such methods.
 *
 *  Example portion of Startup-Sequence script:
 *  
 *             C:DateChk
 *             if WARN
 *                 sys:prefs/time
 *             endif
 *
 */

/*  A version string that can be accessed by the version command, so that  */
/*  you can type:  version C:DateChk, and "DateChk 1.00" will come up  */
/*  (at least under 2.x+)  */
char *version="\000$VER:DateChk 1.00\000";

/*  An include to provide required defines  */
#include <exec/memory.h>
#include <dos/dos.h>

/*  Some prototypes for DOS functions so that the compiler doesn't complain  */
/*  see comment below  */
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>

/*  Some global variables that can be used from any routine in the program  */
struct FileInfoBlock *fib=0;
struct Lock *lock=0;
struct DateStamp datestamp;
BPTR fp;

/*  Prototype for our cleanup function.  It is listed below main  */
/*  This is required if you use functions before you formally declare  */
/*  them.  It stops the compiler from complaining :)  */
void Free( void );

int main( int argc, char **argv )
    {
    BYTE buffer[2];

    /*  The file info block cannot be just declared like:
     *      struct FileInfoBlock fib
     *  since it must be longword aligned (on a 4 byte boundary) for some
     *  stupid reason.  So we must allocate it with the AllocMem function,
     *  although under 2.x+ AllocDosObject would also do fine.
     */

    fib = (struct FileInfoBlock *) AllocMem( sizeof( struct FileInfoBlock ), MEMF_PUBLIC | MEMF_CLEAR );
    if( !fib )
        {
        /*  To run out of memory on such a small allocation during startup
         *  generally would mean a reasonably drastic system failure.  So a
         *  FAIL (20) is returned
         */
        Free();
        return( 20 );
        }

    /*  Next get a lock on the file  */
    lock = (struct Lock *) Lock( argv[0], EXCLUSIVE_LOCK );
    if( !lock )
        {
        /*  To not be able to get a lock on the program that was just called
         *  is also a pretty serious case
         */
        Free();
        return( 20 );
        }

    if( !Examine( (BPTR) lock, fib ) )
        {
        /*  AmigaDOS could not examine the lock correctly  */
        Free();
        return( 20 );
        }

    /*  Get the current system time  */
    DateStamp( &datestamp );

    /*  Now compare the two, to ensure that the current time
     *  is *after* the time stored in the fib structure
     */

    if( datestamp.ds_Days == fib->fib_Date.ds_Days )
        {
        /*  We have not changed days, so check the minutes  */
        if( datestamp.ds_Minute == fib->fib_Date.ds_Minute )
            {
            /*  We have not changed minutes, so check ticks  */
            if( datestamp.ds_Tick < fib->fib_Date.ds_Minute )
                {
                /*  Time seems to have travelled backwards  */

                /*  Return a warn code  */
                Free();
                return( 5 );
                }
            }
        if( datestamp.ds_Minute < fib->fib_Date.ds_Minute )
            {
            /*  Time seems to have travelled backwards  */
            Free();
            return( 5 );
            }
        }

    if( datestamp.ds_Days < fib->fib_Date.ds_Days )
        {
        /*  Time seems to have travelled backwards  */
        Free();
        return( 5 );
        }

    /*  If we got through this far, then everything is OK  */
    /*  update the datestamp of the program  */

    /*  Free up some of the things we allocated earlier  */
    Free();

    fp = Open( argv[0], MODE_READWRITE );
    if( !fp )
        {
        /*  Something has gone wrong again  */
        /*  But not being able to save the data is not  */
        /*  a punisable offence  */
        return( 0 );
        }

    /*  For the file to register as modified, we must write to it  */
    /*  what better to write than itself  */

    Seek( fp, 0, OFFSET_BEGINNING );    /*  This line is not really reqd  */

    if( !Read( fp, &buffer, 1L ) )
        {
        /*  Could not read a single byte of the file  */
        Close( fp );
        return( 20 );
        }

    Seek( fp, 0, OFFSET_BEGINNING );    /*  This is  */

    if( !Write( fp, &buffer, 1L ) )
        {
        /*  Could not write back.  No harm done  */
        Close( fp );
        return( 0 );
        }

    Close( fp );

    /*  Now return a success code  */
    return( 0 );
    }

void Free( void )
    {
    if( lock )
        UnLock( (BPTR) lock );
    if( fib )
        FreeMem( (APTR) fib, sizeof( struct FileInfoBlock ) );
    }
