001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.codec.cli; 018 019import java.io.File; 020import java.io.IOException; 021import java.nio.charset.Charset; 022import java.security.MessageDigest; 023import java.util.Arrays; 024import java.util.Locale; 025 026import org.apache.commons.codec.binary.Hex; 027import org.apache.commons.codec.digest.DigestUtils; 028import org.apache.commons.codec.digest.MessageDigestAlgorithms; 029 030/** 031 * A minimal command line to run digest over files, directories or a string 032 * 033 * @see #main(String[]) 034 * @since 1.11 035 */ 036public class Digest { 037 038 /** 039 * Runs the digest algorithm in {@code args[0]} on the file in {@code args[1]}. If there is no {@code args[1]}, use 040 * standard input. 041 * 042 * <p> 043 * The algorithm can also be {@code ALL} or {@code *} to output one line for each known algorithm. 044 * </p> 045 * 046 * @param args 047 * {@code args[0]} is one of {@link MessageDigestAlgorithms} name, 048 * {@link MessageDigest} name, {@code ALL}, or {@code *}. 049 * {@code args[1+]} is a FILE/DIRECTORY/String. 050 * @throws IOException if an error occurs 051 */ 052 public static void main(final String[] args) throws IOException { 053 new Digest(args).run(); 054 } 055 056 private final String algorithm; 057 private final String[] args; 058 private final String[] inputs; 059 060 private Digest(final String[] args) { 061 if (args == null) { 062 throw new IllegalArgumentException("args"); 063 } 064 if (args.length == 0) { 065 throw new IllegalArgumentException( 066 String.format("Usage: java %s [algorithm] [FILE|DIRECTORY|string] ...", Digest.class.getName())); 067 } 068 this.args = args; 069 algorithm = args[0]; 070 if (args.length <= 1) { 071 inputs = null; 072 } else { 073 inputs = new String[args.length -1]; 074 System.arraycopy(args, 1, inputs, 0, inputs.length); 075 } 076 } 077 078 private void println(final String prefix, final byte[] digest) { 079 println(prefix, digest, null); 080 } 081 082 private void println(final String prefix, final byte[] digest, final String fileName) { 083 // The standard appears to be to print 084 // hex, space, then either space or '*' followed by filename 085 // where '*' is used for binary files 086 // shasum(1) has a -b option which generates " *" separator 087 // we don't distinguish binary files at present 088 System.out.println(prefix + Hex.encodeHexString(digest) + (fileName != null ? " " + fileName : "")); 089 } 090 091 private void run() throws IOException { 092 if (algorithm.equalsIgnoreCase("ALL") || algorithm.equals("*")) { 093 run(MessageDigestAlgorithms.values()); 094 return; 095 } 096 final MessageDigest messageDigest = DigestUtils.getDigest(algorithm, null); 097 if (messageDigest != null) { 098 run("", messageDigest); 099 } else { 100 run("", DigestUtils.getDigest(algorithm.toUpperCase(Locale.ROOT))); 101 } 102 } 103 104 private void run(final String[] digestAlgorithms) throws IOException { 105 for (final String messageDigestAlgorithm : digestAlgorithms) { 106 if (DigestUtils.isAvailable(messageDigestAlgorithm)) { 107 run(messageDigestAlgorithm + " ", messageDigestAlgorithm); 108 } 109 } 110 } 111 112 private void run(final String prefix, final MessageDigest messageDigest) throws IOException { 113 if (inputs == null) { 114 println(prefix, DigestUtils.digest(messageDigest, System.in)); 115 return; 116 } 117 for(final String source : inputs) { 118 final File file = new File(source); 119 if (file.isFile()) { 120 println(prefix, DigestUtils.digest(messageDigest, file), source); 121 } else if (file.isDirectory()) { 122 final File[] listFiles = file.listFiles(); 123 if (listFiles != null) { 124 run(prefix, messageDigest, listFiles); 125 } 126 } else { 127 // use the default charset for the command-line parameter 128 final byte[] bytes = source.getBytes(Charset.defaultCharset()); 129 println(prefix, DigestUtils.digest(messageDigest, bytes)); 130 } 131 } 132 } 133 134 private void run(final String prefix, final MessageDigest messageDigest, final File[] files) throws IOException { 135 for (final File file : files) { 136 if (file.isFile()) { 137 println(prefix, DigestUtils.digest(messageDigest, file), file.getName()); 138 } 139 } 140 } 141 142 private void run(final String prefix, final String messageDigestAlgorithm) throws IOException { 143 run(prefix, DigestUtils.getDigest(messageDigestAlgorithm)); 144 } 145 146 @Override 147 public String toString() { 148 return String.format("%s %s", super.toString(), Arrays.toString(args)); 149 } 150}