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; 025import java.util.Objects; 026 027import org.apache.commons.codec.binary.Hex; 028import org.apache.commons.codec.digest.DigestUtils; 029import org.apache.commons.codec.digest.MessageDigestAlgorithms; 030 031/** 032 * A minimal command line to run digest over files, directories or a string. 033 * 034 * @see #main(String[]) 035 * @since 1.11 036 */ 037public class Digest { 038 039 /** 040 * Runs the digest algorithm in {@code args[0]} on the file in {@code args[1]}. If there is no {@code args[1]}, use 041 * standard input. 042 * 043 * <p> 044 * The algorithm can also be {@code ALL} or {@code *} to output one line for each known algorithm. 045 * </p> 046 * 047 * @param args 048 * {@code args[0]} is one of {@link MessageDigestAlgorithms} name, 049 * {@link MessageDigest} name, {@code ALL}, or {@code *}. 050 * {@code args[1+]} is a FILE/DIRECTORY/String. 051 * @throws IOException if an error occurs 052 */ 053 public static void main(final String[] args) throws IOException { 054 new Digest(args).run(); 055 } 056 057 private final String algorithm; 058 private final String[] args; 059 private final String[] inputs; 060 061 private Digest(final String[] args) { 062 Objects.requireNonNull(args); 063 final int argsLength = args.length; 064 if (argsLength == 0) { 065 throw new IllegalArgumentException( 066 String.format("Usage: java %s [algorithm] [FILE|DIRECTORY|string] ...", Digest.class.getName())); 067 } 068 this.args = args; 069 this.algorithm = args[0]; 070 if (argsLength <= 1) { 071 this.inputs = null; 072 } else { 073 this.inputs = Arrays.copyOfRange(args, 1, argsLength); 074 } 075 } 076 077 private void println(final String prefix, final byte[] digest) { 078 println(prefix, digest, null); 079 } 080 081 private void println(final String prefix, final byte[] digest, final String fileName) { 082 // The standard appears to be to print 083 // hex, space, then either space or '*' followed by file name 084 // where '*' is used for binary files 085 // shasum(1) has a -b option which generates " *" separator 086 // we don't distinguish binary files at present 087 System.out.println(prefix + Hex.encodeHexString(digest) + (fileName != null ? " " + fileName : "")); 088 } 089 090 private void run() throws IOException { 091 if (algorithm.equalsIgnoreCase("ALL") || algorithm.equals("*")) { 092 run(MessageDigestAlgorithms.values()); 093 return; 094 } 095 final MessageDigest messageDigest = DigestUtils.getDigest(algorithm, null); 096 if (messageDigest != null) { 097 run("", messageDigest); 098 } else { 099 run("", DigestUtils.getDigest(algorithm.toUpperCase(Locale.ROOT))); 100 } 101 } 102 103 private void run(final String prefix, final MessageDigest messageDigest) throws IOException { 104 if (inputs == null) { 105 println(prefix, DigestUtils.digest(messageDigest, System.in)); 106 return; 107 } 108 for (final String source : inputs) { 109 final File file = new File(source); 110 if (file.isFile()) { 111 println(prefix, DigestUtils.digest(messageDigest, file), source); 112 } else if (file.isDirectory()) { 113 final File[] listFiles = file.listFiles(); 114 if (listFiles != null) { 115 run(prefix, messageDigest, listFiles); 116 } 117 } else { 118 // use the default charset for the command-line parameter 119 final byte[] bytes = source.getBytes(Charset.defaultCharset()); 120 println(prefix, DigestUtils.digest(messageDigest, bytes)); 121 } 122 } 123 } 124 125 private void run(final String prefix, final MessageDigest messageDigest, final File[] files) throws IOException { 126 for (final File file : files) { 127 if (file.isFile()) { 128 println(prefix, DigestUtils.digest(messageDigest, file), file.getName()); 129 } 130 } 131 } 132 133 private void run(final String prefix, final String messageDigestAlgorithm) throws IOException { 134 run(prefix, DigestUtils.getDigest(messageDigestAlgorithm)); 135 } 136 137 private void run(final String[] digestAlgorithms) throws IOException { 138 for (final String messageDigestAlgorithm : digestAlgorithms) { 139 if (DigestUtils.isAvailable(messageDigestAlgorithm)) { 140 run(messageDigestAlgorithm + " ", messageDigestAlgorithm); 141 } 142 } 143 } 144 145 @Override 146 public String toString() { 147 return String.format("%s %s", super.toString(), Arrays.toString(args)); 148 } 149}