#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<signal.h>
#include<time.h>
#include<string.h>
//
//  This code is likely to be incorrect and should not be used
//

#define FILELENGTH 511
//  A correct version should not have any limit on the file size.
//  This version might fail if names are very long, but it is easier to
//  write
#define DEPTH 0
//  Not to be set to a value larger than 0 until your program appears to
//  be working

struct backup {
    int exists ;
    time_t lastfull ;
    time_t lastinc ;
    time_t latestattempt ;
} ;

void SaveOldBKP( char *dir , int depth )
{
    char nwd[FILELENGTH] ;
    char buf[FILELENGTH] ;
    char tmp[FILELENGTH] ;
    char file[FILELENGTH] ;
    FILE *fp ;
    struct stat filestat ;
    int ret ;

    sprintf( nwd , "%s/.bkp" , dir ) ;
    sprintf( tmp , "ls -s %s" , dir ) ;
    system( tmp ) ;
    ret = mkdir( nwd , S_IRWXU ) ;
    if( depth > 0 && ret < 0 )
        SaveOldBKP( nwd , depth - 1 ) ;
    printf( "S:Copying files from old backup %s %s %d\n" , dir , nwd , depth ) ;
    sprintf( tmp , "%s/.tmp%d" , dir , getpid() ) ;
    sprintf( buf , "ls %s > %s" , dir , tmp ) ;
    system( buf ) ;
    fp = fopen( tmp , "r" ) ;
    while( 1 ) {
        ret = fscanf( fp , "%s" , file ) ;
        if( ret > 0 ) {
            sprintf( buf , "%s/%s" , dir , file ) ;
            printf( "Stating file %s\n" , buf ) ;
            if( (ret = lstat( buf , &filestat )) < 0 ) {
                perror( "stat" ) ;
                kill( getpid() , SIGINT ) ;
            } else if( S_ISREG( filestat.st_mode ) ) {
                sprintf( buf , "cp %s/%s %s/%s" , dir , file , nwd , file ) ;
                if( system( buf ) == -1 ) {
                    perror( "system" ) ;
                    kill( getpid() , SIGINT ) ;
                }
            }
        } else
            break ;
    }
    unlink( tmp ) ;
}

struct backup *PrepareBKP( char *dir , int depth ) 
{
    int fd ;
    char cwd[FILELENGTH] ;
    char header[FILELENGTH ] ;
    int ret ;

    struct backup *b = (struct backup *) malloc( sizeof( struct backup ) ) ;

    printf( "Backup for directory %s (%d)\n" , dir , depth ) ;
    if( (fd = open( dir , O_RDONLY , 0 )) == -1 ) {
        printf( "Cannot open directory: %s\n" , dir ) ;
        kill( getpid() , SIGINT ) ;
    }
    close( fd ) ;

    sprintf( cwd , "%s/.bkp" , dir ) ;
    sprintf( header , "%s/header" , cwd ) ;
    ret = mkdir( cwd , S_IRWXU ) ;
    if( ret == 0 ) {
        printf( "No header found\n" ) ;
        fd = open( header , O_CREAT | O_EXCL | O_RDWR , 0600 ) ;
        b->lastinc = 0 ;
        b->lastfull = 0 ;
        b->exists = 0 ;
    } else {
        printf( "file %s exists?\n" , header ) ;
        fd = open( header , O_RDWR , 0 ) ;
        if( fd < 0 ) {
            perror( "opening bkp/header failed" ) ;
            kill( getpid() , SIGINT ) ;
        }
        ret = read( fd , b , sizeof( struct backup ) ) ;
        if( ret != sizeof( struct backup ) )
            printf( "Length error %d %d\n" , ret , sizeof( struct backup ) ) ;
        if( depth > 0 ) 
            SaveOldBKP( cwd , depth-1 ) ;
        b->exists = 1 ;
    }

    time( &(b->latestattempt) ) ;
    printf( "current time = %s" , asctime( localtime( &(b->latestattempt) ) ) ) ;
    printf( "last backup = %s\n" , asctime( localtime( &(b->lastinc) ) ) ) ;
    if( (ret = lseek( fd , 0 , SEEK_SET )) < 0 )
        perror( "seek" ) ;
    write( fd , b , sizeof( struct backup ) ) ;
    close( fd ) ;
    return b ;
}

void Directory( char *dir )
{
    int fd ;
    char bfn[FILELENGTH] ;
    char file[FILELENGTH] ;
    char buf[FILELENGTH*2] ;
    char tmp[FILELENGTH] ;
    char header[FILELENGTH] ;
    int ret ;
    FILE *fp ;
    int mode = 0 ;
    struct backup *backup ;
    struct stat filestat ;
    char d ;
    
    backup = PrepareBKP( dir , DEPTH ) ;
    if( backup->exists ) printf( "Backup exists\n" ) ;

    printf( "Scanning directory %s\n" , dir ) ;

    sprintf( tmp , "%s/.tmp%d" , dir , getpid() ) ;
    sprintf( buf , "ls %s > %s" , dir , tmp ) ;
    if( system( buf ) == -1 ) {
        perror( "system" ) ;
        kill( getpid() , SIGINT ) ;
    }
    fp = fopen( tmp , "r" ) ;
    while( 1 ) {
        ret = fscanf( fp , "%[^\t\n]%c" , file , &d ) ;
        if( ret > 0 ) {
            printf( "%s:" , file ) ;
            sprintf( buf , "%s/%s" , dir , file ) ;
            sprintf( bfn , "%s/.bkp/%s" , dir , file ) ;
            strcpy( file , buf ) ;
            if( (ret = lstat( file , &filestat )) < 0 ) {
                perror( "stat" ) ;
                printf( "Failed stating file %s\n" , file ) ;
                sprintf( buf , "ls -l %s" , file ) ;
                system( buf ) ;
                kill( getpid() , SIGINT ) ;
            } else {
                if( S_ISDIR( filestat.st_mode ) ) {
                    printf( "(directory)" ) ;
                    Directory( file ) ;
                }
                if( S_ISREG( filestat.st_mode ) ) {
                    printf( "(regular)" ) ;
                    if( filestat.st_mtime > backup->lastinc ) {
                        printf( "===> Backup needed:::" ) ;
                        sprintf( buf , "cp %s %s" , file , bfn ) ;
                        system( buf ) ;
                        printf( "copied %s to %s\n" , file , bfn ) ;
                    }
                }
                if( S_ISLNK( filestat.st_mode ) )
                    printf( "(link)" ) ;
                printf( " last modified %s" 
                 , asctime( localtime(&filestat.st_mtime)) ) ;
            }
        } else
            break ;
    }
    unlink( tmp ) ;

    sprintf( header , "%s/.bkp/header" , dir ) ;
    fd = open( header , O_RDWR , 0 ) ;
    if( fd < 0 ) {
        perror( "opening bkp/header failed" ) ;
        kill( getpid() , SIGINT ) ;
    }
    ret = read( fd , backup , sizeof( struct backup ) ) ;
    if( ret != sizeof( struct backup ) )
        printf( "Length error %d %d\n" , ret , sizeof( struct backup ) ) ;
    time( &(backup->lastinc) ) ;
    if( (ret = lseek( fd , 0 , SEEK_SET )) < 0 )
        perror( "seek" ) ;
    write( fd , backup , sizeof( struct backup ) ) ;
    close( fd ) ;
    printf( "End of backup for %s\n" , dir ) ;
}

int main( int argc , char **argv )
{
    if( argc < 2 )
        Directory( "./dummy" ) ;
    else
        Directory( argv[1] ) ;
}

