From: <cr...@us...> - 2008-11-03 14:22:34
|
Revision: 4665 http://jnode.svn.sourceforge.net/jnode/?rev=4665&view=rev Author: crawley Date: 2008-11-03 14:22:31 +0000 (Mon, 03 Nov 2008) Log Message: ----------- Fixed the syntax and argument parsing for 'md5sum' to work as intended. Added support for md5summing the input stream. Improved documentation strings, and generally tidied up the code. Modified Paths: -------------- trunk/fs/descriptors/org.jnode.fs.command.xml trunk/fs/src/fs/org/jnode/fs/command/Md5SumCommand.java Modified: trunk/fs/descriptors/org.jnode.fs.command.xml =================================================================== --- trunk/fs/descriptors/org.jnode.fs.command.xml 2008-11-03 14:05:07 UTC (rev 4664) +++ trunk/fs/descriptors/org.jnode.fs.command.xml 2008-11-03 14:22:31 UTC (rev 4665) @@ -94,6 +94,17 @@ <empty description="list the current directory"/> <repeat><argument argLabel="path" description="list files or directories"/></repeat> </syntax> + <syntax alias="md5sum"> + <option description="Checks MD5 digests recorded in a file" + argLabel="checkfile" shortName="c" longName="check"/> + <sequence description="Calculates the MD5 digests for files"> + <optional eager="true"> + <option argLabel="recursive" shortName="r" longName="recursive"/> + </optional> + <repeat minCount="1"><argument argLabel="paths"/></repeat> + </sequence> + <empty description="Calculates the MD5 digest for standard input"/> + </syntax> <syntax alias="mkdir"> <argument argLabel="directory" description="create a new directory"/> </syntax> @@ -111,21 +122,8 @@ <syntax alias="touch"> <argument argLabel="file" description="touch the given file"/> </syntax> - <syntax alias="md5sum"> - <sequence description="Cecks md5 sums agains a file"> - <option argLabel="check" shortName="c" longName="check"/> - <argument argLabel="checkfile" description="md5sum file to check against"/> - </sequence> - <sequence description="Calculates the md5sum for files or folders of files"> - <optionSet> - <option argLabel="recursive" shortName="r" longName="recursive"/> - </optionSet> - <repeat minCount="1"><argument argLabel="paths"/></repeat> - </sequence> - </syntax> </extension> - <extension point="org.jnode.security.permissions"> <permission class="java.io.FilePermission" name="<<ALL FILES>>" actions="read,write,delete"/> <permission class="java.net.SocketPermission" name="*:0-" actions="connect,resolve"/> Modified: trunk/fs/src/fs/org/jnode/fs/command/Md5SumCommand.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/command/Md5SumCommand.java 2008-11-03 14:05:07 UTC (rev 4664) +++ trunk/fs/src/fs/org/jnode/fs/command/Md5SumCommand.java 2008-11-03 14:22:31 UTC (rev 4665) @@ -1,6 +1,25 @@ +/* + * $Id: FormatCommand.java 3585 2007-11-13 13:31:18Z galatnm $ + * + * JNode.org + * Copyright (C) 2003-2006 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.fs.command; -import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -8,221 +27,233 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; +import java.io.PrintWriter; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; + import org.jnode.shell.AbstractCommand; -import org.jnode.shell.CommandLine; 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 cr...@jn... + */ public class Md5SumCommand extends AbstractCommand { private final FileArgument ARG_PATHS = new FileArgument( "paths", Argument.OPTIONAL | Argument.MULTIPLE, - "the files or directories to be md5summed"); + "the files (or directories) to be calculate MD5 digests for"); private final FlagArgument FLAG_RECURSIVE = new FlagArgument( "recursive", Argument.OPTIONAL, - "if set, will recursively go through all folders, md5summing all files"); - private final FlagArgument ARG_CHECK = new FlagArgument( - "check", Argument.OPTIONAL, - "if set, will check all md5sums against a file "); + "if set, recursively calculate MD5 digests for the contents of any directory"); private final FileArgument ARG_CHECKFILE = new FileArgument( "checkfile", Argument.OPTIONAL | Argument.SINGLE, - "the md5sum file to check against "); + "check MD5 digests for files listed in this file"); - private static final int MEGABYTE = 1048576; - static final byte[] HEX_CHAR_TABLE = { - (byte) '0', (byte) '1', (byte) '2', (byte) '3', - (byte) '4', (byte) '5', (byte) '6', (byte) '7', - (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', - (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' - }; + private static final int BUFFER_SIZE = 1048576; // 1Mb - private boolean recursive; - private File[] paths; - private PrintStream err; - private MessageDigest msgDigest; - private PrintStream out; - private File checkFile; - private String hexOutputString; - + private PrintWriter out; + private PrintWriter err; + private MessageDigest digestEngine; + + public Md5SumCommand() { - super("Calculate a md5sum against a file or Folder of Files"); - registerArguments(ARG_PATHS, FLAG_RECURSIVE, ARG_CHECK, ARG_CHECKFILE); + super("Calculate or check MD5 digests"); + registerArguments(ARG_PATHS, FLAG_RECURSIVE, ARG_CHECKFILE); } + public void execute() throws Exception { + this.err = getError().getPrintWriter(); + this.out = getOutput().getPrintWriter(); - public void execute(CommandLine commandLine, InputStream in, - PrintStream out, PrintStream err) throws Exception { + // If this throws an exception, we want it to propagate ,,, + digestEngine = MessageDigest.getInstance("md5"); - recursive = FLAG_RECURSIVE.isSet(); - paths = ARG_PATHS.getValues(); - checkFile = ARG_CHECKFILE.getValue(); - - this.err = err; - this.out = out; - - if (ARG_CHECK.isSet()) { - calculatemd5sumAgainstFile(); - } else { - boolean ok = true; + boolean ok = true; + if (ARG_CHECKFILE.isSet()) { + ok = checkFile(ARG_CHECKFILE.getValue()); + } else if (ARG_PATHS.isSet()) { + boolean recursive = FLAG_RECURSIVE.isSet(); + File[] paths = ARG_PATHS.getValues(); for (File file : paths) { - ok &= calculatemd5sum(file); + ok &= processFile(file, recursive); } + } else { + ok = processInput(); } - - + if (!ok) { + exit(1); + } } - private void calculatemd5sumAgainstFile() { - boolean md5Ok = true; - if (!checkFile.exists()) { - err.println(checkFile); - md5Ok = false; - } - - BufferedReader reader = null; - if (md5Ok) { + /** + * 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 { - reader = new BufferedReader(new FileReader(checkFile)); - } catch (FileNotFoundException e) { - //have just checked against this, so this shouldnt occour + br = new BufferedReader(new FileReader(checkFile)); + } catch (FileNotFoundException ex) { + err.println("Cannot open " + checkFile + ": " + ex.getMessage()); + return false; } - } - - if (md5Ok) { String readLine; - try { - int failCount = 0; - while ((readLine = reader.readLine()) != null) { - boolean passed = true; - String[] line = readLine.split("[ ]+"); - if (calculatemd5sum(new File(line[1].trim()))) { - if (!hexOutputString.equals(line[0].trim())) { - passed = false; + 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++; } - } else { - passed = false; - } - - if (passed) { - out.println(line[1] + " : OK"); - } else { - out.println(line[1] + " : FAILED"); + } catch (IOException ex) { + out.println(line[1] + " : IO EXCEPTION - " + ex.getMessage()); failCount++; } } - if (failCount > 0) { - out.println(failCount + " file(s) failed"); + } + if (failCount > 0) { + err.println(failCount + " file(s) failed"); + return false; + } + } catch (IOException ex) { + err.println("problem reading file " + checkFile + ": " + ex.getMessage()); + return false; + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException ex) { + // squash ... } - } catch (IOException e) { - out.println("md5sum could not be checked against " + checkFile); } } + return true; } - - private boolean calculatemd5sum(File file) { - if (!file.exists()) { - err.println(file + " does not exist"); - return false; - } - boolean md5sumOk = 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("..")) { - md5sumOk &= calculatemd5sum(f); + res &= processFile(f, recursive); } } } else { err.println("Cannot calculate md5sum on folder: " + file); - md5sumOk = false; + res = false; } } else { - if (md5sumOk) { - if (msgDigest == null) { - try { - msgDigest = MessageDigest.getInstance("md5"); - } catch (NoSuchAlgorithmException e) { - err.println("md5sum algorithm Could not be found"); - md5sumOk = false; - } - } else { - msgDigest.reset(); - } + try { + out.println(toHexString(computeDigest(file)) + " " + file); } - - BufferedInputStream bis = null; - byte[] buffer = null; - int maxBytesToRead = 0; - long fileSize = file.length(); - if (md5sumOk) { - if (fileSize >= MEGABYTE) { - maxBytesToRead = MEGABYTE; - buffer = new byte[maxBytesToRead]; - } else { - maxBytesToRead = (int) fileSize; - buffer = new byte[maxBytesToRead]; - } - try { - bis = new BufferedInputStream(new FileInputStream(file)); - } catch (FileNotFoundException e) { - md5sumOk = false; - } - + catch (IOException ex) { + err.println(file + " was not md5summed: " + ex.getMessage()); } + } + 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.println("Input was not md5summed: " + ex.getMessage()); + return false; + } + } - - if (md5sumOk) { - int bytesRead = 0; - try { - bytesRead = bis.read(buffer, 0, maxBytesToRead); - do { - msgDigest.update(buffer); - bytesRead = bis.read(buffer, 0, maxBytesToRead); - } while (bytesRead > 0); - bis.close(); - hexOutputString = convertOutputToHexString(msgDigest.digest()); - } catch (IOException e) { - md5sumOk = 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); } - - if (md5sumOk) { - out.println(hexOutputString + " " + file); - } else { - err.println(file + " was not md5summed"); + return digestEngine.digest(); + } finally { + if (file != null && is != null) { + is.close(); } - } - - return md5sumOk; } - - private String convertOutputToHexString(byte[] bs) throws UnsupportedEncodingException { - byte[] hex = new byte[2 * bs.length]; + /** + * 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 : bs) { - int v = b & 0xFF; - hex[index++] = HEX_CHAR_TABLE[v >>> 4]; - hex[index++] = HEX_CHAR_TABLE[v & 0xF]; + for (byte b : digest) { + hex[index++] = hexChar(b >> 4); + hex[index++] = hexChar(b); } - - return new String(hex, "ASCII"); + 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 */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |