// Revision history
//
// 1.16 - Long time lasting version
// 1.17 - 21 Dec 2000 - Added variable sleepTime - Time to sleep
// 1.18 - 22 Dec 2000 - Added extra yield
// 1.19 - 26 Mar 2002 - Added -s: Show option, -1: Run once option
// 1.20 - 26 Dec 2002 - Cleanup, uses mirror.dat file
// 1.21 -  6 Feb 2005 - Removed -i, added update date and length checks

import java.io.*;
import java.util.*;


class mirror 
{
    static String basePath = "";
    static String fromDir = "";
    static String toDir = "";
    static String datafileName = "mirror.dat";
    static byte bytebuffer[];
    static int bytesize = 100000;
    static boolean changeSpacesToSymbol = true;
    static String hexchar[];
    static FileWriter resultFile;
    static Hashtable fileList;
    static boolean index_only = false;

    static int VITALS_OFF    = 0;
    static int VITALS_UPDATE = 1;
    static int VITALS_ERROR  = 2;

    static int sleepTime = 15; // Sleep for xx minutes

    static String str_doesNotExist = " does not exist";
    static String str_noBasePath = "Could not determine base path";
    static String str_existsIsAFile = " exists and is a file";
    static String str_couldNotCreateDirectory = ": Could not create Directory";
    static String str_updating = ": Updating";
    static String str_updated = ": Updated";
    static String str_files = " files";
    static String str_canonicalException = ": Canonical exception";
    static String str_goButton = ">>";

    public static void main( String argv[] )
    {
        new mirror( argv );
    }

    public mirror( String argv[])
    {
        int inx;
        boolean running = true;

        if ( argv.length < 3 )
        {
            System.out.println("Usage: java mirror <source dir> <dest dir> <mirror file>");
            System.out.println("    -n: Convert spaces (%20)");
        }

        fromDir = argv[0];
        toDir = argv[1];
        datafileName = argv[2];

        if ( argv.length > 3 )
        {
            if ( argv[3].equals( "-n") )
            {
                changeSpacesToSymbol = true;
            }
        }

        bytebuffer = new byte[bytesize];

        hexchar = new String[16];
        hexchar[0] = "0";
        hexchar[1] = "1";
        hexchar[2] = "2";
        hexchar[3] = "3";
        hexchar[4] = "4";
        hexchar[5] = "5";
        hexchar[6] = "6";
        hexchar[7] = "7";
        hexchar[8] = "8";
        hexchar[9] = "9";
        hexchar[10] = "a";
        hexchar[11] = "b";
        hexchar[12] = "c";
        hexchar[13] = "d";
        hexchar[14] = "e";
        hexchar[15] = "f";

        fileList = new Hashtable( 1000, 0.5f);

        // Load current records

        loadLastMirror();

        try
        {
            resultFile = new FileWriter( datafileName );

            // Run the mirror

            basePath  = "";
    
            execMirror( fromDir, toDir, 0 ); 

            // End

            resultFile.close();
        }
        catch ( java.io.IOException ioexception )
        {
            // Cannot do anything, really
        }
    }

    public void loadLastMirror()
    {
        FileReader readfile;
        StringBuffer filename;
        StringBuffer updatetime;
        boolean loadingFilename = true;
        char buffer[];
        int inx;
        int charsRead;
        char readChar;
        int bufferSize = 100000;

        buffer = new char[bufferSize];

        try
        {
            readfile = new FileReader( datafileName );

            charsRead = readfile.read( buffer, 0, bufferSize );
            filename = new StringBuffer();
            updatetime = new StringBuffer();

            while ( charsRead != -1 )
            {
                for ( inx = 0; inx < charsRead; inx++ )
                {
                    readChar = buffer[inx];

                    if ( readChar == '\n' )
                    {
                        if ( !loadingFilename )
                        {
                            fileList.put( filename.toString(),
                                          updatetime.toString() );
                            filename = new StringBuffer();
                            updatetime = new StringBuffer();
                        }
                        loadingFilename = !loadingFilename;
                    }
                    else if ( loadingFilename )
                    {
                        filename.append( readChar );
                    }
                    else
                    {
                        updatetime.append( readChar );
                    }
                }

                charsRead = readfile.read( buffer, 0, bufferSize );
            }

            readfile.close();
        }
        catch ( java.io.IOException ioe )
        {
            // If we cannot read the file, it probably does not exist
        }
    }


    String printChar( char x )
    {
        return ( hexchar[x >> 4 & 0x0f] + hexchar[x >> 0 & 0x0f] );
    }
 

    public boolean execMirror( String fromDir, String toDir, int level )
    {
        boolean retval = true; // Terminated ok
        int inx = 0;
        StringBuffer convertedString;
        int convertParse;
        File y;
        String lastUpdate;

        File x = new File( fromDir );

        if ( changeSpacesToSymbol )
        {
            convertedString = new StringBuffer();

            for ( convertParse = 0; convertParse < toDir.length(); convertParse++ )
            {
                if ( ( toDir.charAt( convertParse ) < 0x21 ) ||
                     ( toDir.charAt( convertParse ) > 0x7a ) )
                {
                    convertedString.append( "%"+printChar( toDir.charAt(convertParse) ) );
                }
                else
                {
                    convertedString.append( toDir.charAt( convertParse ) );
                }
            }

            toDir = convertedString.toString();
            y = new File( toDir );
        }
        else
        { 
            y = new File( toDir );
        }

        if ( !x.exists() )
        {
            System.out.println( x.getName() + str_doesNotExist );
            return retval;
        }

        // If this is the first time, save off the base path so we
        // don't check linked directories

        if ( level == 0 )
        {
            try 
            {
                basePath = x.getCanonicalPath();
            }
            catch ( Exception e1 )
            {
                retval = false;
                System.out.println( str_noBasePath );
            }
        }



        if ( x.isDirectory() )
        {
            // Do not recurse into linked directories

            try 
            {
                if ( !x.getCanonicalPath().startsWith( basePath ) )
                {
                    return retval;
                }
            }
            catch ( Exception e2 ) 
            { 
                System.out.println( x.getName() + str_canonicalException );
                return false;
            }

            if ( y.exists() )
            {
                if ( !( y.isDirectory() ) ) // y is a file, and already exists
                {
                    System.out.println( y.getName() + str_existsIsAFile );
                    return false; // Could not do it.
                }
            }
            else // Create a directory
            {
                if ( !( y.mkdir() ) ) // Otherwise, created
                { 
                    System.out.println( y.getName() + 
                                        str_couldNotCreateDirectory );
                    return false; // Done with this directory
                }
            }

            String names[] = x.list();

            try
            {
                while ( true )
                {
                    if ( !execMirror( x.getAbsolutePath()+x.separator+names[inx],
                               y.getAbsolutePath()+y.separator+names[inx],
                             level+1 ) )
                    {
                        retval = false;
                    }

                    inx++;
                }
            }
            catch ( Exception endOfList ) 
            { 
                //System.out.println("Exception end of list");
            }
        }
        else // It was a file
        {
            // If the last update is different or nonexistant, replace

            lastUpdate = (String)fileList.get( fromDir );

            if ( ( lastUpdate == null ) ||
                 ( !lastUpdate.equals( Long.toString( x.lastModified() ) ) ) ) 
            {
                copyFile( x, y );
            }

            try
            {
                // In any case, write the current files values
                resultFile.write( fromDir + "\n"+x.lastModified() + "\n");
            }
            catch ( java.io.IOException writeException )
            {
                // Cannot do anything...
            }
        }

        return retval;
    }

    void copyFile( File fromFile, File toFile )
    {
        int data;
        int result = 1;

        if ( ( fromFile.lastModified() == toFile.lastModified() ) &&
             ( fromFile.length() == toFile.length() ) )
        {
            System.out.println( "Skipping "+fromFile.toString() );
            return;
        }

        System.out.println( "Update "+fromFile.toString() );

        try
        { 
            BufferedInputStream fi = new BufferedInputStream ( new FileInputStream( fromFile ) );
            BufferedOutputStream fo = new BufferedOutputStream ( new FileOutputStream( toFile ) );

            while ( result != -1 )
            {
                result = fi.read( bytebuffer, 0, bytesize );

                if ( result != -1 )
                {
                    fo.write( bytebuffer, 0, result );
                }
            }

            fi.close();
            fo.close();

            toFile.setLastModified( fromFile.lastModified() );
        } 
        catch ( Exception e4 )
        { 
            System.out.println("Buffered exception");
        } 
    }
}
