Package org.jnode.command.file

Source Code of org.jnode.command.file.Md5SumCommand

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.command.file;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.security.MessageDigest;

import org.jnode.shell.AbstractCommand;
import org.jnode.shell.syntax.Argument;
import org.jnode.shell.syntax.FileArgument;
import org.jnode.shell.syntax.FlagArgument;

/**
* This command class calculates MD5 digests for files.  If the 'check' flag is
* given, it reads a file containing digests and filenames and checks that the
* named files digests batch the supplied digests.  Otherwise, it simply calculates
* and outputs the digests as filenames in a format compatible with 'check' processing.
* <p>
* The command is based on the GNU 'md5sum' command, with some differences in the
* 'check' file format.
*
* @author Tim Sparg
* @author crawley@jnode.org
*/
public class Md5SumCommand extends AbstractCommand {

    private static final String help_paths = "the files (or directories) to calculate the MD5 digests for";
    private static final String help_recurse = "if set, recursively calculate the MD5 digests for the contents of " +
                                               "a directory";
    private static final String help_check = "check the MD5 digests for files listed in this file";
    private static final String help_super = "Calculate or check MD5 digests";
    private static final String fmt_err_open = "Cannot open %s: %s%n";
    @SuppressWarnings("unused")
    private static final String str_ok = "Ok";
    @SuppressWarnings("unused")
    private static final String str_fail = "Failed";
    private static final String fmt_io_ex = "%s : IO Exception - %s%n";
    private static final String fmt_fail_count = "%d file(s) failed%n";
    private static final String fmt_err_read = "Problem reading file %s: %s";
    private static final String err_norecurse = "Cannot calculate MD5 on directory, use recurse flag";
    private static final String fmt_err_md5 = "%s was not processed: %s%n";
    private static final String str_input = "Input";
   
    private final FileArgument argPaths;
    private final FlagArgument flagRecursive;
    private final FileArgument argCheckfile;


    private static final int BUFFER_SIZE = 1048576// 1Mb

    private PrintWriter out;
    private PrintWriter err;
    private MessageDigest digestEngine;
   
   
    public Md5SumCommand() {
        super(help_super);
        argPaths      = new FileArgument("paths", Argument.MULTIPLE | Argument.EXISTING, help_paths);
        flagRecursive = new FlagArgument("recursive", Argument.OPTIONAL, help_recurse);
        argCheckfile  = new FileArgument("checkfile", Argument.SINGLE | Argument.EXISTING, help_check);
        registerArguments(argPaths, flagRecursive, argCheckfile);
    }

    public void execute() throws Exception {
        this.err = getError().getPrintWriter();
        this.out = getOutput().getPrintWriter();

        // If this throws an exception, we want it to propagate ,,,
        digestEngine = MessageDigest.getInstance("md5");

        boolean ok = true;
        if (argCheckfile.isSet()) {
            ok = checkFile(argCheckfile.getValue());
        } else if (argPaths.isSet()) {
            boolean recursive = flagRecursive.isSet();
            File[] paths = argPaths.getValues();
            for (File file : paths) {
                ok &= processFile(file, recursive);
            }
        } else {
            ok = processInput();
        }
        if (!ok) {
            exit(1);
        }
    }

    /**
     * Read a check file containing a list of filenames and the expected MD5 digests
     * of the corresponding files.  We calculate the actual digests, compare with
     * the expected digests and report any differences and other problems to stdout
     * and/or stderr.
     *
     * @param checkFile the file listing the files to be checked and their
     *        expected digests.
     * @return <code>true</code> if the check file was opened successfully, all
     *         named files were found and their digests were as expected.
     */
    private boolean checkFile(File checkFile) {
        BufferedReader br = null;
        try {
            try {
                br = new BufferedReader(new FileReader(checkFile));
            } catch (FileNotFoundException ex) {
                err.format(fmt_err_open, checkFile, ex.getLocalizedMessage());
                return false;
            }
            String readLine;
            int failCount = 0;
            while ((readLine = br.readLine()) != null) {
                String[] line = readLine.split("\\s+");
                if (line.length == 2) {
                    try {
                        byte[] digest = computeDigest(new File(line[1]));
                        boolean passed = toHexString(digest).equalsIgnoreCase(line[0]);
                        if (passed) {
                            out.println(line[1] + " : OK");
                        } else {
                            out.println(line[1] + " : FAILED");
                            failCount++;
                        }
                    } catch (IOException ex) {
                        out.format(fmt_io_ex, line[1], ex.getLocalizedMessage());
                        failCount++;
                    }
                }
            }
            if (failCount > 0) {
                err.format(fmt_fail_count, failCount);
                return false;
            }
        } catch (IOException ex) {
            err.format(fmt_err_read, checkFile, ex.getLocalizedMessage());
            return false;
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException ex) {
                    // squash ...
                }
            }
        }
        return true;
    }

    /**
     * Calculate the digest for a file and output it in a format compatible
     * with the 'check' option.  If the supplied file is a directory and
     * 'recursive' is <code>true</code>, recursively process the directory
     * contents.
     *
     * @param file the file (or directory) to be processed.
     * @param recursive if <code>true</code> process directory contents recursively.
     * @return <code>true</code> if all file digests were computed and output.
     */
    private boolean processFile(File file, boolean recursive) {
        boolean res = true;
        if (file.isDirectory()) {
            if (recursive) {
                for (File f : file.listFiles()) {
                    final String name = f.getName();
                    if (!name.equals(".") && !name.equals("..")) {
                        res &= processFile(f, recursive);
                    }
                }
            } else {
                err.println(err_norecurse);
                res = false;
            }
        } else {
            try {
                out.println(toHexString(computeDigest(file)) + "    " + file);
            } catch (IOException ex) {
                err.format(fmt_err_md5, file, ex.getLocalizedMessage());
            }
        }
        return res;
    }
   
    /**
     * Compute digest for the command's input stream.
     * @return <code>true</code> if all is well;
     */
    private boolean processInput() {
        try {
            out.println(toHexString(computeDigest(null)));
            return true;
        } catch (IOException ex) {
            err.format(fmt_err_md5, str_input, ex.getLocalizedMessage());
            return false;
        }
    }

    /**
     * Compute the digest of a file.
     * @param file the file.  If this is <code>null</code>, we calculate the digest
     *       for the command's input stream.
     * @return the digest.
     * @throws IOException
     */
    private byte[] computeDigest(File file) throws IOException {
        InputStream is = null;
        byte[] buffer = new byte[BUFFER_SIZE];
        try {
            digestEngine.reset();
            is = (file != null) ? new FileInputStream(file) : getInput().getInputStream();
            int bytesRead;
            while ((bytesRead = is.read(buffer, 0, BUFFER_SIZE)) > 0) {
                digestEngine.update(buffer, 0, bytesRead);
            }
            return digestEngine.digest();
        } finally {
            if (file != null && is != null) {
                is.close();
            }
        }
    }

    /**
     * Turn a digest represented as a byte array into a hexadecimal string.
     *
     * @param digest the digest as bytes
     * @return the corresponding hex string.
     */
    private String toHexString(byte[] digest) {
        char[] hex = new char[digest.length * 2];
        int index = 0;

        for (byte b : digest) {
            hex[index++] = hexChar(b >> 4);
            hex[index++] = hexChar(b);
        }
        return new String(hex);
    }
   
    private char hexChar(int i) {
        i = i & 0xf;
        return (char) ((i > 9 ? ('a' - 10) : '0') + i);
    }

    /**
     * The classic Java entry point.
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        new Md5SumCommand().execute(args);
    }

}
TOP

Related Classes of org.jnode.command.file.Md5SumCommand

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.