diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java
new file mode 100644
index 000000000..59446d3ce
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant;
+
+/**
+ * Signals an error condition during a build
+ */
+public class BuildException extends RuntimeException {
+
+ private static final long serialVersionUID = -5419014565354664240L;
+
+ /** Location in the build file where the exception occurred */
+ private Location location = Location.UNKNOWN_LOCATION;
+
+ /**
+ * Constructs a build exception with no descriptive information.
+ */
+ public BuildException() {
+ super();
+ }
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be null.
+ */
+ public BuildException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the given message and exception as
+ * a root cause.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be null unless a cause is specified.
+ * @param cause The exception that might have caused this one.
+ * May be null.
+ */
+ public BuildException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs an exception with the given message and exception as
+ * a root cause and a location in a file.
+ *
+ * @param msg A description of or information about the exception.
+ * Should not be null unless a cause is specified.
+ * @param cause The exception that might have caused this one.
+ * May be null.
+ * @param location The location in the project file where the error
+ * occurred. Must not be null.
+ */
+ public BuildException(String msg, Throwable cause, Location location) {
+ this(msg, cause);
+ this.location = location;
+ }
+
+ /**
+ * Constructs an exception with the given exception as a root cause.
+ *
+ * @param cause The exception that might have caused this one.
+ * Should not be null.
+ */
+ public BuildException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an exception with the given descriptive message and a
+ * location in a file.
+ *
+ * @param message A description of or information about the exception.
+ * Should not be null.
+ * @param location The location in the project file where the error
+ * occurred. Must not be null.
+ */
+ public BuildException(String message, Location location) {
+ super(message);
+ this.location = location;
+ }
+
+ /**
+ * Constructs an exception with the given exception as
+ * a root cause and a location in a file.
+ *
+ * @param cause The exception that might have caused this one.
+ * Should not be null.
+ * @param location The location in the project file where the error
+ * occurred. Must not be null.
+ */
+ public BuildException(Throwable cause, Location location) {
+ this(cause);
+ this.location = location;
+ }
+
+ /**
+ * Returns the nested exception, if any.
+ *
+ * @return the nested exception, or null if no
+ * exception is associated with this one
+ * @deprecated Use {@link #getCause} instead.
+ */
+ public Throwable getException() {
+ return getCause();
+ }
+
+ /**
+ * Returns the location of the error and the error message.
+ *
+ * @return the location of the error and the error message
+ */
+ public String toString() {
+ return location.toString() + getMessage();
+ }
+
+ /**
+ * Sets the file location where the error occurred.
+ *
+ * @param location The file location where the error occurred.
+ * Must not be null.
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the file location where the error occurred.
+ *
+ * @return the file location where the error occurred.
+ */
+ public Location getLocation() {
+ return location;
+ }
+
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java
new file mode 100644
index 000000000..e65d994a0
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java
@@ -0,0 +1,1816 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.ResourceFactory;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileResource;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.FileSelector;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPath;
+import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPattern;
+import org.owasp.dependencycheck.org.apache.tools.ant.util.CollectionUtils;
+import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils;
+import org.owasp.dependencycheck.org.apache.tools.ant.util.SymbolicLinkUtils;
+import org.owasp.dependencycheck.org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Class for scanning a directory for files/directories which match certain criteria.
+ *
+ * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which
+ * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude
+ * files based on their filename.
+ *
+ * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is
+ * matched against a set of selectors, including special support for matching against filenames with include and and
+ * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file
+ * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will
+ * be placed in the list of files/directories found.
+ *
+ * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no
+ * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors
+ * are supplied, none are applied.
+ *
+ * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment
+ * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under
+ * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same
+ * is done for the pattern against which should be matched.
+ *
+ * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in
+ * the pattern, it matches zero or more path segments of the name.
+ *
+ * There is a special case regarding the use of File.separators at the beginning of the pattern and the
+ * string to match:
+ * When a pattern starts with a File.separator, the string to match must also start with a
+ * File.separator. When a pattern does not start with a File.separator, the string to match
+ * may not start with a File.separator. When one of these rules is not obeyed, the string will not match.
+ *
+ * When a name path segment is matched against a pattern path segment, the following special characters can be used:
+ * '*' matches zero or more characters
+ * '?' matches one character.
+ *
+ * Examples:
+ *
+ * "**\*.class" matches all .class files/dirs in a directory tree.
+ *
+ * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a
+ * directory called test.
+ *
+ * "**" matches everything in a directory tree.
+ *
+ * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test
+ * (e.g. "abc\test\def\ghi\XYZ123").
+ *
+ * Case sensitivity may be turned off if necessary. By default, it is turned on.
+ *
This will scan a directory called test for .class files, but excludes all files in all proper subdirectories
+ * of a directory called "modules".
+ *
+ */
+public class DirectoryScanner
+ implements FileScanner, SelectorScanner, ResourceFactory {
+
+ /**
+ * Is OpenVMS the operating system we're running on?
+ */
+ private static final boolean ON_VMS = Os.isFamily("openvms");
+
+ /**
+ * Patterns which should be excluded by default.
+ *
+ *
+ * Note that you can now add patterns to the list of default excludes. Added patterns will not become part of this
+ * array that has only been kept around for backwards compatibility reasons.
+ *
+ * @deprecated since 1.6.x. Use the {@link #getDefaultExcludes getDefaultExcludes} method instead.
+ */
+ protected static final String[] DEFAULTEXCLUDES = {
+ // Miscellaneous typical temporary files
+ SelectorUtils.DEEP_TREE_MATCH + "/*~",
+ SelectorUtils.DEEP_TREE_MATCH + "/#*#",
+ SelectorUtils.DEEP_TREE_MATCH + "/.#*",
+ SelectorUtils.DEEP_TREE_MATCH + "/%*%",
+ SelectorUtils.DEEP_TREE_MATCH + "/._*",
+ // CVS
+ SelectorUtils.DEEP_TREE_MATCH + "/CVS",
+ SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore",
+ // SCCS
+ SelectorUtils.DEEP_TREE_MATCH + "/SCCS",
+ SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH,
+ // Visual SourceSafe
+ SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc",
+ // Subversion
+ SelectorUtils.DEEP_TREE_MATCH + "/.svn",
+ SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH,
+ // Git
+ SelectorUtils.DEEP_TREE_MATCH + "/.git",
+ SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes",
+ SelectorUtils.DEEP_TREE_MATCH + "/.gitignore",
+ SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules",
+ // Mercurial
+ SelectorUtils.DEEP_TREE_MATCH + "/.hg",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgignore",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgsub",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate",
+ SelectorUtils.DEEP_TREE_MATCH + "/.hgtags",
+ // Bazaar
+ SelectorUtils.DEEP_TREE_MATCH + "/.bzr",
+ SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH,
+ SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore",
+ // Mac
+ SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store"
+ };
+
+ /**
+ * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks}
+ *
+ * @since Ant 1.8.0
+ */
+ public static final int MAX_LEVELS_OF_SYMLINKS = 5;
+ /**
+ * The end of the exception message if something that should be there doesn't exist.
+ */
+ public static final String DOES_NOT_EXIST_POSTFIX = " does not exist.";
+
+ /**
+ * Helper.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Helper.
+ */
+ private static final SymbolicLinkUtils SYMLINK_UTILS
+ = SymbolicLinkUtils.getSymbolicLinkUtils();
+
+ /**
+ * Patterns which should be excluded by default.
+ *
+ * @see #addDefaultExcludes()
+ */
+ private static final Set defaultExcludes = new HashSet();
+
+ static {
+ resetDefaultExcludes();
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /**
+ * The base directory to be scanned.
+ */
+ protected File basedir;
+
+ /**
+ * The patterns for the files to be included.
+ */
+ protected String[] includes;
+
+ /**
+ * The patterns for the files to be excluded.
+ */
+ protected String[] excludes;
+
+ /**
+ * Selectors that will filter which files are in our candidate list.
+ */
+ protected FileSelector[] selectors = null;
+
+ /**
+ * The files which matched at least one include and no excludes and were selected.
+ */
+ protected Vector filesIncluded;
+
+ /**
+ * The files which did not match any includes or selectors.
+ */
+ protected Vector filesNotIncluded;
+
+ /**
+ * The files which matched at least one include and at least one exclude.
+ */
+ protected Vector filesExcluded;
+
+ /**
+ * The directories which matched at least one include and no excludes and were selected.
+ */
+ protected Vector dirsIncluded;
+
+ /**
+ * The directories which were found and did not match any includes.
+ */
+ protected Vector dirsNotIncluded;
+
+ /**
+ * The directories which matched at least one include and at least one exclude.
+ */
+ protected Vector dirsExcluded;
+
+ /**
+ * The files which matched at least one include and no excludes and which a selector discarded.
+ */
+ protected Vector filesDeselected;
+
+ /**
+ * The directories which matched at least one include and no excludes but which a selector discarded.
+ */
+ protected Vector dirsDeselected;
+
+ /**
+ * Whether or not our results were built by a slow scan.
+ */
+ protected boolean haveSlowResults = false;
+
+ /**
+ * Whether or not the file system should be treated as a case sensitive one.
+ */
+ protected boolean isCaseSensitive = true;
+
+ /**
+ * Whether a missing base directory is an error.
+ *
+ * @since Ant 1.7.1
+ */
+ protected boolean errorOnMissingDir = true;
+
+ /**
+ * Whether or not symbolic links should be followed.
+ *
+ * @since Ant 1.5
+ */
+ private boolean followSymlinks = true;
+
+ /**
+ * Whether or not everything tested so far has been included.
+ */
+ protected boolean everythingIncluded = true;
+
+ // CheckStyle:VisibilityModifier ON
+ /**
+ * List of all scanned directories.
+ *
+ * @since Ant 1.6
+ */
+ private Set scannedDirs = new HashSet();
+
+ /**
+ * Map of all include patterns that are full file names and don't contain any wildcards.
+ *
+ *
+ * Maps pattern string to TokenizedPath.
+ *
+ *
+ * If this instance is not case sensitive, the file names get turned to upper case.
+ *
+ *
+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).
+ *
+ * @since Ant 1.8.0
+ */
+ private Map includeNonPatterns = new HashMap();
+
+ /**
+ * Map of all exclude patterns that are full file names and don't contain any wildcards.
+ *
+ *
+ * Maps pattern string to TokenizedPath.
+ *
+ *
+ * If this instance is not case sensitive, the file names get turned to upper case.
+ *
+ *
+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).
+ *
+ * @since Ant 1.8.0
+ */
+ private Map excludeNonPatterns = new HashMap();
+
+ /**
+ * Array of all include patterns that contain wildcards.
+ *
+ *
+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).
+ */
+ private TokenizedPattern[] includePatterns;
+
+ /**
+ * Array of all exclude patterns that contain wildcards.
+ *
+ *
+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).
+ */
+ private TokenizedPattern[] excludePatterns;
+
+ /**
+ * Have the non-pattern sets and pattern arrays for in- and excludes been initialized?
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean areNonPatternSetsReady = false;
+
+ /**
+ * Scanning flag.
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean scanning = false;
+
+ /**
+ * Scanning lock.
+ *
+ * @since Ant 1.6.3
+ */
+ private Object scanLock = new Object();
+
+ /**
+ * Slow scanning flag.
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean slowScanning = false;
+
+ /**
+ * Slow scanning lock.
+ *
+ * @since Ant 1.6.3
+ */
+ private Object slowScanLock = new Object();
+
+ /**
+ * Exception thrown during scan.
+ *
+ * @since Ant 1.6.3
+ */
+ private IllegalStateException illegal = null;
+
+ /**
+ * The maximum number of times a symbolic link may be followed during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS;
+
+ /**
+ * Absolute paths of all symlinks that haven't been followed but would have been if followsymlinks had been true or
+ * maxLevelsOfSymlinks had been higher.
+ *
+ * @since Ant 1.8.0
+ */
+ private Set notFollowedSymlinks = new HashSet();
+
+ /**
+ * Sole constructor.
+ */
+ public DirectoryScanner() {
+ }
+
+ /**
+ * Test whether or not a given path matches the start of a given pattern up to the first "**".
+ *
+ * This is not a general purpose test and should only be used if you can live with false positives. For example,
+ * pattern=**\a and str=b will yield true.
+ *
+ * @param pattern The pattern to match against. Must not be null.
+ * @param str The path to match, as a String. Must not be null.
+ *
+ * @return whether or not a given path matches the start of a given pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart(String pattern, String str) {
+ return SelectorUtils.matchPatternStart(pattern, str);
+ }
+
+ /**
+ * Test whether or not a given path matches the start of a given pattern up to the first "**".
+ *
+ * This is not a general purpose test and should only be used if you can live with false positives. For example,
+ * pattern=**\a and str=b will yield true.
+ *
+ * @param pattern The pattern to match against. Must not be null.
+ * @param str The path to match, as a String. Must not be null.
+ * @param isCaseSensitive Whether or not matching should be performed case sensitively.
+ *
+ * @return whether or not a given path matches the start of a given pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart(String pattern, String str,
+ boolean isCaseSensitive) {
+ return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
+ }
+
+ /**
+ * Test whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be null.
+ * @param str The path to match, as a String. Must not be null.
+ *
+ * @return true if the pattern matches against the string, or false otherwise.
+ */
+ protected static boolean matchPath(String pattern, String str) {
+ return SelectorUtils.matchPath(pattern, str);
+ }
+
+ /**
+ * Test whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be null.
+ * @param str The path to match, as a String. Must not be null.
+ * @param isCaseSensitive Whether or not matching should be performed case sensitively.
+ *
+ * @return true if the pattern matches against the string, or false otherwise.
+ */
+ protected static boolean matchPath(String pattern, String str,
+ boolean isCaseSensitive) {
+ return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
+ }
+
+ /**
+ * Test whether or not a string matches against a pattern. The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against. Must not be null.
+ * @param str The string which must be matched against the pattern. Must not be null.
+ *
+ * @return true if the string matches against the pattern, or false otherwise.
+ */
+ public static boolean match(String pattern, String str) {
+ return SelectorUtils.match(pattern, str);
+ }
+
+ /**
+ * Test whether or not a string matches against a pattern. The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against. Must not be null.
+ * @param str The string which must be matched against the pattern. Must not be null.
+ * @param isCaseSensitive Whether or not matching should be performed case sensitively.
+ *
+ *
+ * @return true if the string matches against the pattern, or false otherwise.
+ */
+ protected static boolean match(String pattern, String str,
+ boolean isCaseSensitive) {
+ return SelectorUtils.match(pattern, str, isCaseSensitive);
+ }
+
+ /**
+ * Get the list of patterns that should be excluded by default.
+ *
+ * @return An array of String based on the current contents of the defaultExcludes
+ * Set.
+ *
+ * @since Ant 1.6
+ */
+ public static String[] getDefaultExcludes() {
+ synchronized (defaultExcludes) {
+ return (String[]) defaultExcludes.toArray(new String[defaultExcludes
+ .size()]);
+ }
+ }
+
+ /**
+ * Add a pattern to the default excludes unless it is already a default exclude.
+ *
+ * @param s A string to add as an exclude pattern.
+ * @return true if the string was added; false if it already existed.
+ *
+ * @since Ant 1.6
+ */
+ public static boolean addDefaultExclude(String s) {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.add(s);
+ }
+ }
+
+ /**
+ * Remove a string if it is a default exclude.
+ *
+ * @param s The string to attempt to remove.
+ * @return true if s was a default exclude (and thus was removed); false if
+ * s was not in the default excludes list to begin with.
+ *
+ * @since Ant 1.6
+ */
+ public static boolean removeDefaultExclude(String s) {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.remove(s);
+ }
+ }
+
+ /**
+ * Go back to the hardwired default exclude patterns.
+ *
+ * @since Ant 1.6
+ */
+ public static void resetDefaultExcludes() {
+ synchronized (defaultExcludes) {
+ defaultExcludes.clear();
+ for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
+ defaultExcludes.add(DEFAULTEXCLUDES[i]);
+ }
+ }
+ }
+
+ /**
+ * Set the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\'
+ * characters are replaced by File.separatorChar, so the separator used need not match
+ * File.separatorChar.
+ *
+ * @param basedir The base directory to scan.
+ */
+ public void setBasedir(String basedir) {
+ setBasedir(basedir == null ? (File) null
+ : new File(basedir.replace('/', File.separatorChar).replace(
+ '\\', File.separatorChar)));
+ }
+
+ /**
+ * Set the base directory to be scanned. This is the directory which is scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ */
+ public synchronized void setBasedir(File basedir) {
+ this.basedir = basedir;
+ }
+
+ /**
+ * Return the base directory to be scanned. This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned.
+ */
+ public synchronized File getBasedir() {
+ return basedir;
+ }
+
+ /**
+ * Find out whether include exclude patterns are matched in a case sensitive way.
+ *
+ * @return whether or not the scanning is case sensitive.
+ * @since Ant 1.6
+ */
+ public synchronized boolean isCaseSensitive() {
+ return isCaseSensitive;
+ }
+
+ /**
+ * Set whether or not include and exclude patterns are matched in a case sensitive way.
+ *
+ * @param isCaseSensitive whether or not the file system should be regarded as a case sensitive one.
+ */
+ public synchronized void setCaseSensitive(boolean isCaseSensitive) {
+ this.isCaseSensitive = isCaseSensitive;
+ }
+
+ /**
+ * Sets whether or not a missing base directory is an error
+ *
+ * @param errorOnMissingDir whether or not a missing base directory is an error
+ * @since Ant 1.7.1
+ */
+ public void setErrorOnMissingDir(boolean errorOnMissingDir) {
+ this.errorOnMissingDir = errorOnMissingDir;
+ }
+
+ /**
+ * Get whether or not a DirectoryScanner follows symbolic links.
+ *
+ * @return flag indicating whether symbolic links should be followed.
+ *
+ * @since Ant 1.6
+ */
+ public synchronized boolean isFollowSymlinks() {
+ return followSymlinks;
+ }
+
+ /**
+ * Set whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed.
+ */
+ public synchronized void setFollowSymlinks(boolean followSymlinks) {
+ this.followSymlinks = followSymlinks;
+ }
+
+ /**
+ * The maximum number of times a symbolic link may be followed during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMaxLevelsOfSymlinks(int max) {
+ maxLevelsOfSymlinks = max;
+ }
+
+ /**
+ * Set the list of include patterns to use. All '/' and '\' characters are replaced by
+ * File.separatorChar, so the separator used need not match File.separatorChar.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param includes A list of include patterns. May be null, indicating that all files should be
+ * included. If a non-null list is given, all elements must be non-null.
+ */
+ public synchronized void setIncludes(String[] includes) {
+ if (includes == null) {
+ this.includes = null;
+ } else {
+ this.includes = new String[includes.length];
+ for (int i = 0; i < includes.length; i++) {
+ this.includes[i] = normalizePattern(includes[i]);
+ }
+ }
+ }
+
+ public synchronized void setIncludes(String include) {
+ if (include == null) {
+ this.includes = null;
+ } else {
+ this.includes = new String[1];
+ this.includes[0] = normalizePattern(include);
+ }
+ }
+
+ /**
+ * Set the list of exclude patterns to use. All '/' and '\' characters are replaced by
+ * File.separatorChar, so the separator used need not match File.separatorChar.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param excludes A list of exclude patterns. May be null, indicating that no files should be
+ * excluded. If a non-null list is given, all elements must be non-null.
+ */
+ public synchronized void setExcludes(String[] excludes) {
+ if (excludes == null) {
+ this.excludes = null;
+ } else {
+ this.excludes = new String[excludes.length];
+ for (int i = 0; i < excludes.length; i++) {
+ this.excludes[i] = normalizePattern(excludes[i]);
+ }
+ }
+ }
+
+ /**
+ * Add to the list of exclude patterns to use. All '/' and '\' characters are replaced by
+ * File.separatorChar, so the separator used need not match File.separatorChar.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param excludes A list of exclude patterns. May be null, in which case the exclude patterns don't
+ * get changed at all.
+ *
+ * @since Ant 1.6.3
+ */
+ public synchronized void addExcludes(String[] excludes) {
+ if (excludes != null && excludes.length > 0) {
+ if (this.excludes != null && this.excludes.length > 0) {
+ String[] tmp = new String[excludes.length
+ + this.excludes.length];
+ System.arraycopy(this.excludes, 0, tmp, 0,
+ this.excludes.length);
+ for (int i = 0; i < excludes.length; i++) {
+ tmp[this.excludes.length + i]
+ = normalizePattern(excludes[i]);
+ }
+ this.excludes = tmp;
+ } else {
+ setExcludes(excludes);
+ }
+ }
+ }
+
+ /**
+ * All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match
+ * File.separatorChar.
+ *
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @since Ant 1.6.3
+ */
+ private static String normalizePattern(String p) {
+ String pattern = p.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ if (pattern.endsWith(File.separator)) {
+ pattern += SelectorUtils.DEEP_TREE_MATCH;
+ }
+ return pattern;
+ }
+
+ /**
+ * Set the selectors that will select the filelist.
+ *
+ * @param selectors specifies the selectors to be invoked on a scan.
+ */
+ public synchronized void setSelectors(FileSelector[] selectors) {
+ this.selectors = selectors;
+ }
+
+ /**
+ * Return whether or not the scanner has included all the files or directories it has come across so far.
+ *
+ * @return true if all files and directories which have been found so far have been included.
+ */
+ public synchronized boolean isEverythingIncluded() {
+ return everythingIncluded;
+ }
+
+ /**
+ * Scan for files which match at least one include pattern and don't match any exclude patterns. If there are
+ * selectors then the files must pass muster there, as well. Scans under basedir, if set; otherwise the include
+ * patterns without leading wildcards specify the absolute paths of the files that may be included.
+ *
+ * @exception IllegalStateException if the base directory was set incorrectly (i.e. if it doesn't exist or isn't a
+ * directory).
+ */
+ public void scan() throws IllegalStateException {
+ synchronized (scanLock) {
+ if (scanning) {
+ while (scanning) {
+ try {
+ scanLock.wait();
+ } catch (InterruptedException e) {
+ continue;
+ }
+ }
+ if (illegal != null) {
+ throw illegal;
+ }
+ return;
+ }
+ scanning = true;
+ }
+ File savedBase = basedir;
+ try {
+ synchronized (this) {
+ illegal = null;
+ clearResults();
+
+ // set in/excludes to reasonable defaults if needed:
+ boolean nullIncludes = (includes == null);
+ includes = nullIncludes
+ ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes;
+ boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
+
+ if (basedir != null && !followSymlinks
+ && SYMLINK_UTILS.isSymbolicLink(basedir)) {
+ notFollowedSymlinks.add(basedir.getAbsolutePath());
+ basedir = null;
+ }
+
+ if (basedir == null) {
+ // if no basedir and no includes, nothing to do:
+ if (nullIncludes) {
+ return;
+ }
+ } else {
+ if (!basedir.exists()) {
+ if (errorOnMissingDir) {
+ illegal = new IllegalStateException("basedir "
+ + basedir
+ + DOES_NOT_EXIST_POSTFIX);
+ } else {
+ // Nothing to do - basedir does not exist
+ return;
+ }
+ } else if (!basedir.isDirectory()) {
+ illegal = new IllegalStateException("basedir "
+ + basedir
+ + " is not a"
+ + " directory.");
+ }
+ if (illegal != null) {
+ throw illegal;
+ }
+ }
+ if (isIncluded(TokenizedPath.EMPTY_PATH)) {
+ if (!isExcluded(TokenizedPath.EMPTY_PATH)) {
+ if (isSelected("", basedir)) {
+ dirsIncluded.addElement("");
+ } else {
+ dirsDeselected.addElement("");
+ }
+ } else {
+ dirsExcluded.addElement("");
+ }
+ } else {
+ dirsNotIncluded.addElement("");
+ }
+ checkIncludePatterns();
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ basedir = savedBase;
+ synchronized (scanLock) {
+ scanning = false;
+ scanLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * This routine is actually checking all the include patterns in order to avoid scanning everything under base dir.
+ *
+ * @since Ant 1.6
+ */
+ private void checkIncludePatterns() {
+ ensureNonPatternSetsReady();
+ Map newroots = new HashMap();
+
+ // put in the newroots map the include patterns without
+ // wildcard tokens
+ for (int i = 0; i < includePatterns.length; i++) {
+ String pattern = includePatterns[i].toString();
+ if (!shouldSkipPattern(pattern)) {
+ newroots.put(includePatterns[i].rtrimWildcardTokens(),
+ pattern);
+ }
+ }
+ for (Map.Entry entry : includeNonPatterns.entrySet()) {
+ String pattern = entry.getKey();
+ if (!shouldSkipPattern(pattern)) {
+ newroots.put(entry.getValue(), pattern);
+ }
+ }
+
+ if (newroots.containsKey(TokenizedPath.EMPTY_PATH)
+ && basedir != null) {
+ // we are going to scan everything anyway
+ scandir(basedir, "", true);
+ } else {
+ File canonBase = null;
+ if (basedir != null) {
+ try {
+ canonBase = basedir.getCanonicalFile();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ // only scan directories that can include matched files or
+ // directories
+ for (Map.Entry entry : newroots.entrySet()) {
+ TokenizedPath currentPath = entry.getKey();
+ String currentelement = currentPath.toString();
+ if (basedir == null
+ && !FileUtils.isAbsolutePath(currentelement)) {
+ continue;
+ }
+ File myfile = new File(basedir, currentelement);
+
+ if (myfile.exists()) {
+ // may be on a case insensitive file system. We want
+ // the results to show what's really on the disk, so
+ // we need to double check.
+ try {
+ String path = (basedir == null)
+ ? myfile.getCanonicalPath()
+ : FILE_UTILS.removeLeadingPath(canonBase,
+ myfile.getCanonicalFile());
+ if (!path.equals(currentelement) || ON_VMS) {
+ myfile = currentPath.findFile(basedir, true);
+ if (myfile != null && basedir != null) {
+ currentelement = FILE_UTILS.removeLeadingPath(
+ basedir, myfile);
+ if (!currentPath.toString()
+ .equals(currentelement)) {
+ currentPath
+ = new TokenizedPath(currentelement);
+ }
+ }
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
+ File f = currentPath.findFile(basedir, false);
+ if (f != null && f.exists()) {
+ // adapt currentelement to the case we've
+ // actually found
+ currentelement = (basedir == null)
+ ? f.getAbsolutePath()
+ : FILE_UTILS.removeLeadingPath(basedir, f);
+ myfile = f;
+ currentPath = new TokenizedPath(currentelement);
+ }
+ }
+
+ if (myfile != null && myfile.exists()) {
+ if (!followSymlinks && currentPath.isSymlink(basedir)) {
+ accountForNotFollowedSymlink(currentPath, myfile);
+ continue;
+ }
+ if (myfile.isDirectory()) {
+ if (isIncluded(currentPath)
+ && currentelement.length() > 0) {
+ accountForIncludedDir(currentPath, myfile, true);
+ } else {
+ scandir(myfile, currentPath, true);
+ }
+ } else if (myfile.isFile()) {
+ String originalpattern = (String) entry.getValue();
+ boolean included = isCaseSensitive()
+ ? originalpattern.equals(currentelement)
+ : originalpattern.equalsIgnoreCase(currentelement);
+ if (included) {
+ accountForIncludedFile(currentPath, myfile);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * true if the pattern specifies a relative path without basedir or an absolute path not inside basedir.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean shouldSkipPattern(String pattern) {
+ if (FileUtils.isAbsolutePath(pattern)) {
+ //skip abs. paths not under basedir, if set:
+ if (basedir != null
+ && !SelectorUtils.matchPatternStart(pattern,
+ basedir.getAbsolutePath(),
+ isCaseSensitive())) {
+ return true;
+ }
+ } else if (basedir == null) {
+ //skip non-abs. paths if basedir == null:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Clear the result caches for a scan.
+ */
+ protected synchronized void clearResults() {
+ filesIncluded = new VectorSet();
+ filesNotIncluded = new VectorSet();
+ filesExcluded = new VectorSet();
+ filesDeselected = new VectorSet();
+ dirsIncluded = new VectorSet();
+ dirsNotIncluded = new VectorSet();
+ dirsExcluded = new VectorSet();
+ dirsDeselected = new VectorSet();
+ everythingIncluded = (basedir != null);
+ scannedDirs.clear();
+ notFollowedSymlinks.clear();
+ }
+
+ /**
+ * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories,
+ * whereas a fast scan will only have full results for included files, as it ignores directories which can't
+ * possibly hold any included files/directories.
+ *
+ * Returns immediately if a slow scan has already been completed.
+ */
+ protected void slowScan() {
+ synchronized (slowScanLock) {
+ if (haveSlowResults) {
+ return;
+ }
+ if (slowScanning) {
+ while (slowScanning) {
+ try {
+ slowScanLock.wait();
+ } catch (InterruptedException e) {
+ // Empty
+ }
+ }
+ return;
+ }
+ slowScanning = true;
+ }
+ try {
+ synchronized (this) {
+
+ // set in/excludes to reasonable defaults if needed:
+ boolean nullIncludes = (includes == null);
+ includes = nullIncludes
+ ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes;
+ boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
+
+ String[] excl = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(excl);
+
+ String[] notIncl = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(notIncl);
+
+ ensureNonPatternSetsReady();
+
+ processSlowScan(excl);
+ processSlowScan(notIncl);
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } finally {
+ synchronized (slowScanLock) {
+ haveSlowResults = true;
+ slowScanning = false;
+ slowScanLock.notifyAll();
+ }
+ }
+ }
+
+ private void processSlowScan(String[] arr) {
+ for (int i = 0; i < arr.length; i++) {
+ TokenizedPath path = new TokenizedPath(arr[i]);
+ if (!couldHoldIncluded(path) || contentsExcluded(path)) {
+ scandir(new File(basedir, arr[i]), path, false);
+ }
+ }
+ }
+
+ /**
+ * Scan the given directory for files and directories. Found files and directories are placed in their respective
+ * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is
+ * scanned recursively.
+ *
+ * @param dir The directory to scan. Must not be null.
+ * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using
+ * dir). Must not be null.
+ * @param fast Whether or not this call is part of a fast scan.
+ *
+ * @see #filesIncluded
+ * @see #filesNotIncluded
+ * @see #filesExcluded
+ * @see #dirsIncluded
+ * @see #dirsNotIncluded
+ * @see #dirsExcluded
+ * @see #slowScan
+ */
+ protected void scandir(File dir, String vpath, boolean fast) {
+ scandir(dir, new TokenizedPath(vpath), fast);
+ }
+
+ /**
+ * Scan the given directory for files and directories. Found files and directories are placed in their respective
+ * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is
+ * scanned recursively.
+ *
+ * @param dir The directory to scan. Must not be null.
+ * @param path The path relative to the base directory (needed to prevent problems with an absolute path when using
+ * dir). Must not be null.
+ * @param fast Whether or not this call is part of a fast scan.
+ *
+ * @see #filesIncluded
+ * @see #filesNotIncluded
+ * @see #filesExcluded
+ * @see #dirsIncluded
+ * @see #dirsNotIncluded
+ * @see #dirsExcluded
+ * @see #slowScan
+ */
+ private void scandir(File dir, TokenizedPath path, boolean fast) {
+ if (dir == null) {
+ throw new BuildException("dir must not be null.");
+ }
+ String[] newfiles = dir.list();
+ if (newfiles == null) {
+ if (!dir.exists()) {
+ throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX);
+ } else if (!dir.isDirectory()) {
+ throw new BuildException(dir + " is not a directory.");
+ } else {
+ throw new BuildException("IO error scanning directory '"
+ + dir.getAbsolutePath() + "'");
+ }
+ }
+ scandir(dir, path, fast, newfiles, new LinkedList());
+ }
+
+ private void scandir(File dir, TokenizedPath path, boolean fast,
+ String[] newfiles, LinkedList directoryNamesFollowed) {
+ String vpath = path.toString();
+ if (vpath.length() > 0 && !vpath.endsWith(File.separator)) {
+ vpath += File.separator;
+ }
+
+ // avoid double scanning of directories, can only happen in fast mode
+ if (fast && hasBeenScanned(vpath)) {
+ return;
+ }
+ if (!followSymlinks) {
+ ArrayList noLinks = new ArrayList();
+ for (int i = 0; i < newfiles.length; i++) {
+ try {
+ if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) {
+ String name = vpath + newfiles[i];
+ File file = new File(dir, newfiles[i]);
+ if (file.isDirectory()) {
+ dirsExcluded.addElement(name);
+ } else if (file.isFile()) {
+ filesExcluded.addElement(name);
+ }
+ accountForNotFollowedSymlink(name, file);
+ } else {
+ noLinks.add(newfiles[i]);
+ }
+ } catch (IOException ioe) {
+ String msg = "IOException caught while checking "
+ + "for links, couldn't get canonical path!";
+ // will be caught and redirected to Ant's logging system
+ System.err.println(msg);
+ noLinks.add(newfiles[i]);
+ }
+ }
+ newfiles = (String[]) (noLinks.toArray(new String[noLinks.size()]));
+ } else {
+ directoryNamesFollowed.addFirst(dir.getName());
+ }
+
+ for (int i = 0; i < newfiles.length; i++) {
+ String name = vpath + newfiles[i];
+ TokenizedPath newPath = new TokenizedPath(path, newfiles[i]);
+ File file = new File(dir, newfiles[i]);
+ String[] children = file.list();
+ if (children == null || (children.length == 0 && file.isFile())) {
+ if (isIncluded(newPath)) {
+ accountForIncludedFile(newPath, file);
+ } else {
+ everythingIncluded = false;
+ filesNotIncluded.addElement(name);
+ }
+ } else if (file.isDirectory()) { // dir
+
+ if (followSymlinks
+ && causesIllegalSymlinkLoop(newfiles[i], dir,
+ directoryNamesFollowed)) {
+ // will be caught and redirected to Ant's logging system
+ System.err.println("skipping symbolic link "
+ + file.getAbsolutePath()
+ + " -- too many levels of symbolic"
+ + " links.");
+ notFollowedSymlinks.add(file.getAbsolutePath());
+ continue;
+ }
+
+ if (isIncluded(newPath)) {
+ accountForIncludedDir(newPath, file, fast, children,
+ directoryNamesFollowed);
+ } else {
+ everythingIncluded = false;
+ dirsNotIncluded.addElement(name);
+ if (fast && couldHoldIncluded(newPath)
+ && !contentsExcluded(newPath)) {
+ scandir(file, newPath, fast, children,
+ directoryNamesFollowed);
+ }
+ }
+ if (!fast) {
+ scandir(file, newPath, fast, children, directoryNamesFollowed);
+ }
+ }
+ }
+
+ if (followSymlinks) {
+ directoryNamesFollowed.removeFirst();
+ }
+ }
+
+ /**
+ * Process included file.
+ *
+ * @param name path of the file relative to the directory of the FileSet.
+ * @param file included File.
+ */
+ private void accountForIncludedFile(TokenizedPath name, File file) {
+ processIncluded(name, file, filesIncluded, filesExcluded,
+ filesDeselected);
+ }
+
+ /**
+ * Process included directory.
+ *
+ * @param name path of the directory relative to the directory of the FileSet.
+ * @param file directory as File.
+ * @param fast whether to perform fast scans.
+ */
+ private void accountForIncludedDir(TokenizedPath name, File file,
+ boolean fast) {
+ processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name, fast);
+ }
+ }
+
+ private void accountForIncludedDir(TokenizedPath name,
+ File file, boolean fast,
+ String[] children,
+ LinkedList directoryNamesFollowed) {
+ processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name, fast, children, directoryNamesFollowed);
+ }
+ }
+
+ private void accountForNotFollowedSymlink(String name, File file) {
+ accountForNotFollowedSymlink(new TokenizedPath(name), file);
+ }
+
+ private void accountForNotFollowedSymlink(TokenizedPath name, File file) {
+ if (!isExcluded(name)
+ && (isIncluded(name)
+ || (file.isDirectory() && couldHoldIncluded(name)
+ && !contentsExcluded(name)))) {
+ notFollowedSymlinks.add(file.getAbsolutePath());
+ }
+ }
+
+ private void processIncluded(TokenizedPath path,
+ File file, Vector inc, Vector exc,
+ Vector des) {
+ String name = path.toString();
+ if (inc.contains(name) || exc.contains(name) || des.contains(name)) {
+ return;
+ }
+
+ boolean included = false;
+ if (isExcluded(path)) {
+ exc.add(name);
+ } else if (isSelected(name, file)) {
+ included = true;
+ inc.add(name);
+ } else {
+ des.add(name);
+ }
+ everythingIncluded &= included;
+ }
+
+ /**
+ * Test whether or not a name matches against at least one include pattern.
+ *
+ * @param name The name to match. Must not be null.
+ * @return true when the name matches against at least one include pattern, or false
+ * otherwise.
+ */
+ protected boolean isIncluded(String name) {
+ return isIncluded(new TokenizedPath(name));
+ }
+
+ /**
+ * Test whether or not a name matches against at least one include pattern.
+ *
+ * @param name The name to match. Must not be null.
+ * @return true when the name matches against at least one include pattern, or false
+ * otherwise.
+ */
+ private boolean isIncluded(TokenizedPath path) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? includeNonPatterns.containsKey(path.toString())
+ : includeNonPatterns.containsKey(path.toString().toUpperCase())) {
+ return true;
+ }
+ for (int i = 0; i < includePatterns.length; i++) {
+ if (includePatterns[i].matchPath(path, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether or not a name matches the start of at least one include pattern.
+ *
+ * @param name The name to match. Must not be null.
+ * @return true when the name matches against the start of at least one include pattern, or
+ * false otherwise.
+ */
+ protected boolean couldHoldIncluded(String name) {
+ return couldHoldIncluded(new TokenizedPath(name));
+ }
+
+ /**
+ * Test whether or not a name matches the start of at least one include pattern.
+ *
+ * @param tokenizedName The name to match. Must not be null.
+ * @return true when the name matches against the start of at least one include pattern, or
+ * false otherwise.
+ */
+ private boolean couldHoldIncluded(TokenizedPath tokenizedName) {
+ for (int i = 0; i < includePatterns.length; i++) {
+ if (couldHoldIncluded(tokenizedName, includePatterns[i])) {
+ return true;
+ }
+ }
+ for (Iterator iter = includeNonPatterns.values().iterator();
+ iter.hasNext();) {
+ if (couldHoldIncluded(tokenizedName,
+ iter.next().toPattern())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether or not a name matches the start of the given include pattern.
+ *
+ * @param tokenizedName The name to match. Must not be null.
+ * @return true when the name matches against the start of the include pattern, or false
+ * otherwise.
+ */
+ private boolean couldHoldIncluded(TokenizedPath tokenizedName,
+ TokenizedPattern tokenizedInclude) {
+ return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive())
+ && isMorePowerfulThanExcludes(tokenizedName.toString())
+ && isDeeper(tokenizedInclude, tokenizedName);
+ }
+
+ /**
+ * Verify that a pattern specifies files deeper than the level of the specified file.
+ *
+ * @param pattern the pattern to check.
+ * @param name the name to check.
+ * @return whether the pattern is deeper than the name.
+ * @since Ant 1.6.3
+ */
+ private boolean isDeeper(TokenizedPattern pattern, TokenizedPath name) {
+ return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH)
+ || pattern.depth() > name.depth();
+ }
+
+ /**
+ * Find out whether one particular include pattern is more powerful than all the excludes. Note: the power
+ * comparison is based on the length of the include pattern and of the exclude patterns without the wildcards.
+ * Ideally the comparison should be done based on the depth of the match; that is to say how many file separators
+ * have been matched before the first ** or the end of the pattern.
+ *
+ * IMPORTANT : this function should return false "with care".
+ *
+ * @param name the relative path to test.
+ * @return true if there is no exclude pattern more powerful than this include pattern.
+ * @since Ant 1.6
+ */
+ private boolean isMorePowerfulThanExcludes(String name) {
+ final String soughtexclude
+ = name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH;
+ for (int counter = 0; counter < excludePatterns.length; counter++) {
+ if (excludePatterns[counter].toString().equals(soughtexclude)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Test whether all contents of the specified directory must be excluded.
+ *
+ * @param path the path to check.
+ * @return whether all the specified directory's contents are excluded.
+ */
+ /* package */ boolean contentsExcluded(TokenizedPath path) {
+ for (int i = 0; i < excludePatterns.length; i++) {
+ if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH)
+ && excludePatterns[i].withoutLastToken()
+ .matchPath(path, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether or not a name matches against at least one exclude pattern.
+ *
+ * @param name The name to match. Must not be null.
+ * @return true when the name matches against at least one exclude pattern, or false
+ * otherwise.
+ */
+ protected boolean isExcluded(String name) {
+ return isExcluded(new TokenizedPath(name));
+ }
+
+ /**
+ * Test whether or not a name matches against at least one exclude pattern.
+ *
+ * @param name The name to match. Must not be null.
+ * @return true when the name matches against at least one exclude pattern, or false
+ * otherwise.
+ */
+ private boolean isExcluded(TokenizedPath name) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? excludeNonPatterns.containsKey(name.toString())
+ : excludeNonPatterns.containsKey(name.toString().toUpperCase())) {
+ return true;
+ }
+ for (int i = 0; i < excludePatterns.length; i++) {
+ if (excludePatterns[i].matchPath(name, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether a file should be selected.
+ *
+ * @param name the filename to check for selecting.
+ * @param file the java.io.File object for this filename.
+ * @return false when the selectors says that the file should not be selected, true
+ * otherwise.
+ */
+ protected boolean isSelected(String name, File file) {
+ if (selectors != null) {
+ for (int i = 0; i < selectors.length; i++) {
+ if (!selectors[i].isSelected(basedir, name, file)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the names of the files which matched at least one of the include patterns and none of the exclude
+ * patterns. The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the include patterns and none of the exclude
+ * patterns.
+ */
+ public String[] getIncludedFiles() {
+ String[] files;
+ synchronized (this) {
+ if (filesIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ files = new String[filesIncluded.size()];
+ filesIncluded.copyInto(files);
+ }
+ Arrays.sort(files);
+ return files;
+ }
+
+ /**
+ * Return the count of included files.
+ *
+ * @return int.
+ * @since Ant 1.6.3
+ */
+ public synchronized int getIncludedFilesCount() {
+ if (filesIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ return filesIncluded.size();
+ }
+
+ /**
+ * Return the names of the files which matched none of the include patterns. The names are relative to the base
+ * directory. This involves performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the files which matched none of the include patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getNotIncludedFiles() {
+ slowScan();
+ String[] files = new String[filesNotIncluded.size()];
+ filesNotIncluded.copyInto(files);
+ return files;
+ }
+
+ /**
+ * Return the names of the files which matched at least one of the include patterns and at least one of the exclude
+ * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not
+ * already been completed.
+ *
+ * @return the names of the files which matched at least one of the include patterns and at least one of the exclude
+ * patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getExcludedFiles() {
+ slowScan();
+ String[] files = new String[filesExcluded.size()];
+ filesExcluded.copyInto(files);
+ return files;
+ }
+
+ /**
+ *
+ * Return the names of the files which were selected out and therefore not ultimately included.
+ *
+ *
+ * The names are relative to the base directory. This involves performing a slow scan if one has not already been
+ * completed.
+ *
+ * @return the names of the files which were deselected.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getDeselectedFiles() {
+ slowScan();
+ String[] files = new String[filesDeselected.size()];
+ filesDeselected.copyInto(files);
+ return files;
+ }
+
+ /**
+ * Return the names of the directories which matched at least one of the include patterns and none of the exclude
+ * patterns. The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the include patterns and none of the exclude
+ * patterns.
+ */
+ public String[] getIncludedDirectories() {
+ String[] directories;
+ synchronized (this) {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ directories = new String[dirsIncluded.size()];
+ dirsIncluded.copyInto(directories);
+ }
+ Arrays.sort(directories);
+ return directories;
+ }
+
+ /**
+ * Return the count of included directories.
+ *
+ * @return int.
+ * @since Ant 1.6.3
+ */
+ public synchronized int getIncludedDirsCount() {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ return dirsIncluded.size();
+ }
+
+ /**
+ * Return the names of the directories which matched none of the include patterns. The names are relative to the
+ * base directory. This involves performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the directories which matched none of the include patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getNotIncludedDirectories() {
+ slowScan();
+ String[] directories = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(directories);
+ return directories;
+ }
+
+ /**
+ * Return the names of the directories which matched at least one of the include patterns and at least one of the
+ * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has
+ * not already been completed.
+ *
+ * @return the names of the directories which matched at least one of the include patterns and at least one of the
+ * exclude patterns.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getExcludedDirectories() {
+ slowScan();
+ String[] directories = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(directories);
+ return directories;
+ }
+
+ /**
+ *
+ * Return the names of the directories which were selected out and therefore not ultimately included.
+ *
+ *
+ * The names are relative to the base directory. This involves performing a slow scan if one has not already been
+ * completed.
+ *
+ * @return the names of the directories which were deselected.
+ *
+ * @see #slowScan
+ */
+ public synchronized String[] getDeselectedDirectories() {
+ slowScan();
+ String[] directories = new String[dirsDeselected.size()];
+ dirsDeselected.copyInto(directories);
+ return directories;
+ }
+
+ /**
+ * Absolute paths of all symbolic links that haven't been followed but would have been followed had followsymlinks
+ * been true or maxLevelsOfSymlinks been bigger.
+ *
+ * @return sorted array of not followed symlinks
+ * @since Ant 1.8.0
+ * @see #notFollowedSymlinks
+ */
+ public synchronized String[] getNotFollowedSymlinks() {
+ String[] links;
+ synchronized (this) {
+ links = (String[]) notFollowedSymlinks
+ .toArray(new String[notFollowedSymlinks.size()]);
+ }
+ Arrays.sort(links);
+ return links;
+ }
+
+ /**
+ * Add default exclusions to the current exclusions set.
+ */
+ public synchronized void addDefaultExcludes() {
+ int excludesLength = excludes == null ? 0 : excludes.length;
+ String[] newExcludes;
+ String[] defaultExcludesTemp = getDefaultExcludes();
+ newExcludes = new String[excludesLength + defaultExcludesTemp.length];
+ if (excludesLength > 0) {
+ System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
+ }
+ for (int i = 0; i < defaultExcludesTemp.length; i++) {
+ newExcludes[i + excludesLength]
+ = defaultExcludesTemp[i].replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ }
+ excludes = newExcludes;
+ }
+
+ /**
+ * Get the named resource.
+ *
+ * @param name path name of the file relative to the dir attribute.
+ *
+ * @return the resource with the given name.
+ * @since Ant 1.5.2
+ */
+ public synchronized Resource getResource(String name) {
+ return new FileResource(basedir, name);
+ }
+
+ /**
+ * Has the directory with the given path relative to the base directory already been scanned?
+ *
+ *
+ * Registers the given directory as scanned as a side effect.
+ *
+ * @since Ant 1.6
+ */
+ private boolean hasBeenScanned(String vpath) {
+ return !scannedDirs.add(vpath);
+ }
+
+ /**
+ * This method is of interest for testing purposes. The returned Set is live and should not be modified.
+ *
+ * @return the Set of relative directory names that have been scanned.
+ */
+ /* package-private */ Set getScannedDirs() {
+ return scannedDirs;
+ }
+
+ /**
+ * Clear internal caches.
+ *
+ * @since Ant 1.6
+ */
+ private synchronized void clearCaches() {
+ includeNonPatterns.clear();
+ excludeNonPatterns.clear();
+ includePatterns = null;
+ excludePatterns = null;
+ areNonPatternSetsReady = false;
+ }
+
+ /**
+ * Ensure that the in|exclude "patterns" have been properly divided up.
+ *
+ * @since Ant 1.6.3
+ */
+ /* package */ synchronized void ensureNonPatternSetsReady() {
+ if (!areNonPatternSetsReady) {
+ includePatterns = fillNonPatternSet(includeNonPatterns, includes);
+ excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
+ areNonPatternSetsReady = true;
+ }
+ }
+
+ /**
+ * Add all patterns that are not real patterns (do not contain wildcards) to the set and returns the real patterns.
+ *
+ * @param map Map to populate.
+ * @param patterns String[] of patterns.
+ * @since Ant 1.8.0
+ */
+ private TokenizedPattern[] fillNonPatternSet(Map map, String[] patterns) {
+ ArrayList al = new ArrayList(patterns.length);
+ for (int i = 0; i < patterns.length; i++) {
+ if (!SelectorUtils.hasWildcards(patterns[i])) {
+ String s = isCaseSensitive()
+ ? patterns[i] : patterns[i].toUpperCase();
+ map.put(s, new TokenizedPath(s));
+ } else {
+ al.add(new TokenizedPattern(patterns[i]));
+ }
+ }
+ return (TokenizedPattern[]) al.toArray(new TokenizedPattern[al.size()]);
+ }
+
+ /**
+ * Would following the given directory cause a loop of symbolic links deeper than allowed?
+ *
+ *
+ * Can only happen if the given directory has been seen at least more often than allowed during the current scan and
+ * it is a symbolic link and enough other occurrences of the same name higher up are symbolic links that point to
+ * the same place.
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean causesIllegalSymlinkLoop(String dirName, File parent,
+ LinkedList directoryNamesFollowed) {
+ try {
+ if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks
+ && CollectionUtils.frequency(directoryNamesFollowed, dirName)
+ >= maxLevelsOfSymlinks
+ && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) {
+
+ ArrayList files = new ArrayList();
+ File f = FILE_UTILS.resolveFile(parent, dirName);
+ String target = f.getCanonicalPath();
+ files.add(target);
+
+ String relPath = "";
+ for (String dir : directoryNamesFollowed) {
+ relPath += "../";
+ if (dirName.equals(dir)) {
+ f = FILE_UTILS.resolveFile(parent, relPath + dir);
+ files.add(f.getCanonicalPath());
+ if (files.size() > maxLevelsOfSymlinks
+ && CollectionUtils.frequency(files, target)
+ > maxLevelsOfSymlinks) {
+ return true;
+ }
+ }
+ }
+
+ }
+ return false;
+ } catch (IOException ex) {
+ throw new BuildException("Caught error while checking for"
+ + " symbolic links", ex);
+ }
+ }
+
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java
new file mode 100644
index 000000000..2fdd654f5
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant;
+
+import java.io.File;
+
+/**
+ * An interface used to describe the actions required of any type of
+ * directory scanner.
+ *
+ */
+public interface FileScanner {
+ /**
+ * Adds default exclusions to the current exclusions set.
+ */
+ void addDefaultExcludes();
+
+ /**
+ * Returns the base directory to be scanned.
+ * This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned
+ */
+ File getBasedir();
+
+ /**
+ * Returns the names of the directories which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ */
+ String[] getExcludedDirectories();
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ *
+ */
+ String[] getExcludedFiles();
+
+ /**
+ * Returns the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ String[] getIncludedDirectories();
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ String[] getIncludedFiles();
+
+ /**
+ * Returns the names of the directories which matched none of the include
+ * patterns. The names are relative to the base directory.
+ *
+ * @return the names of the directories which matched none of the include
+ * patterns.
+ */
+ String[] getNotIncludedDirectories();
+
+ /**
+ * Returns the names of the files which matched none of the include
+ * patterns. The names are relative to the base directory.
+ *
+ * @return the names of the files which matched none of the include
+ * patterns.
+ */
+ String[] getNotIncludedFiles();
+
+ /**
+ * Scans the base directory for files which match at least one include
+ * pattern and don't match any exclude patterns.
+ *
+ * @exception IllegalStateException if the base directory was set
+ * incorrectly (i.e. if it is null, doesn't exist,
+ * or isn't a directory).
+ */
+ void scan() throws IllegalStateException;
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively. All '/' and '\' characters should be replaced by
+ * File.separatorChar, so the separator used need not match
+ * File.separatorChar.
+ *
+ * @param basedir The base directory to scan.
+ * Must not be null.
+ */
+ void setBasedir(String basedir);
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ * Should not be null.
+ */
+ void setBasedir(File basedir);
+
+ /**
+ * Sets the list of exclude patterns to use.
+ *
+ * @param excludes A list of exclude patterns.
+ * May be null, indicating that no files
+ * should be excluded. If a non-null list is
+ * given, all elements must be non-null.
+ */
+ void setExcludes(String[] excludes);
+
+ /**
+ * Sets the list of include patterns to use.
+ *
+ * @param includes A list of include patterns.
+ * May be null, indicating that all files
+ * should be included. If a non-null
+ * list is given, all elements must be
+ * non-null.
+ */
+ void setIncludes(String[] includes);
+
+ /**
+ * Sets whether or not the file system should be regarded as case sensitive.
+ *
+ * @param isCaseSensitive whether or not the file system should be
+ * regarded as a case sensitive one
+ */
+ void setCaseSensitive(boolean isCaseSensitive);
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java
new file mode 100644
index 000000000..44137ab5b
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.owasp.dependencycheck.org.apache.tools.ant;
+
+import java.io.Serializable;
+import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils;
+import org.xml.sax.Locator;
+
+/**
+ * Stores the location of a piece of text within a file (file name,
+ * line number and column number). Note that the column number is
+ * currently ignored.
+ *
+ */
+public class Location implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /** Name of the file. */
+ private final String fileName;
+ /** Line number within the file. */
+ private final int lineNumber;
+ /** Column number within the file. */
+ private final int columnNumber;
+
+ /** Location to use when one is needed but no information is available */
+ public static final Location UNKNOWN_LOCATION = new Location();
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Creates an "unknown" location.
+ */
+ private Location() {
+ this(null, 0, 0);
+ }
+
+ /**
+ * Creates a location consisting of a file name but no line number or
+ * column number.
+ *
+ * @param fileName The name of the file. May be null,
+ * in which case the location is equivalent to
+ * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}.
+ */
+ public Location(String fileName) {
+ this(fileName, 0, 0);
+ }
+
+ /**
+ * Creates a location from the SAX locator using the system ID as
+ * the filename.
+ *
+ * @param loc Must not be null.
+ *
+ * @since Ant 1.6
+ */
+ public Location(Locator loc) {
+ this(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber());
+ }
+
+ /**
+ * Creates a location consisting of a file name, line number and
+ * column number.
+ *
+ * @param fileName The name of the file. May be null,
+ * in which case the location is equivalent to
+ * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}.
+ *
+ * @param lineNumber Line number within the file. Use 0 for unknown
+ * positions within a file.
+ * @param columnNumber Column number within the line.
+ */
+ public Location(String fileName, int lineNumber, int columnNumber) {
+ if (fileName != null && fileName.startsWith("file:")) {
+ this.fileName = FILE_UTILS.fromURI(fileName);
+ } else {
+ this.fileName = fileName;
+ }
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ }
+
+ /**
+ * @return the filename portion of the location
+ * @since Ant 1.6
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * @return the line number
+ * @since Ant 1.6
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * @return the column number
+ * @since Ant 1.7
+ */
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+
+ /**
+ * Returns the file name, line number, a colon and a trailing space.
+ * An error message can be appended easily. For unknown locations, an
+ * empty string is returned.
+ *
+ * @return a String of the form "fileName:lineNumber: "
+ * if both file name and line number are known,
+ * "fileName: " if only the file name is known,
+ * and the empty string for unknown locations.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ if (fileName != null) {
+ buf.append(fileName);
+
+ if (lineNumber != 0) {
+ buf.append(":");
+ buf.append(lineNumber);
+ }
+
+ buf.append(": ");
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Equality operation.
+ * @param other the object to compare to.
+ * @return true if the other object contains the same information
+ * as this object.
+ * @since Ant 1.6.3
+ */
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ if (!(other.getClass() == getClass())) {
+ return false;
+ }
+ return toString().equals(other.toString());
+ }
+
+ /**
+ * Hash operation.
+ * @return a hash code value for this location.
+ * @since Ant 1.6.3
+ */
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java
new file mode 100644
index 000000000..5d1a9a47b
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant;
+
+import java.io.File;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * A Path tokenizer takes a path and returns the components that make up
+ * that path.
+ *
+ * The path can use path separators of either ':' or ';' and file separators
+ * of either '/' or '\'.
+ *
+ */
+public class PathTokenizer {
+ /**
+ * A tokenizer to break the string up based on the ':' or ';' separators.
+ */
+ private StringTokenizer tokenizer;
+
+ /**
+ * A String which stores any path components which have been read ahead
+ * due to DOS filesystem compensation.
+ */
+ private String lookahead = null;
+
+ /**
+ * A boolean that determines if we are running on Novell NetWare, which
+ * exhibits slightly different path name characteristics (multi-character
+ * volume / drive names)
+ */
+ private boolean onNetWare = Os.isFamily("netware");
+
+ /**
+ * Flag to indicate whether or not we are running on a platform with a
+ * DOS style filesystem
+ */
+ private boolean dosStyleFilesystem;
+
+ /**
+ * Constructs a path tokenizer for the specified path.
+ *
+ * @param path The path to tokenize. Must not be null.
+ */
+ public PathTokenizer(String path) {
+ if (onNetWare) {
+ // For NetWare, use the boolean=true mode, so we can use delimiter
+ // information to make a better decision later.
+ tokenizer = new StringTokenizer(path, ":;", true);
+ } else {
+ // on Windows and Unix, we can ignore delimiters and still have
+ // enough information to tokenize correctly.
+ tokenizer = new StringTokenizer(path, ":;", false);
+ }
+ dosStyleFilesystem = File.pathSeparatorChar == ';';
+ }
+
+ /**
+ * Tests if there are more path elements available from this tokenizer's
+ * path. If this method returns true, then a subsequent call
+ * to nextToken will successfully return a token.
+ *
+ * @return true if and only if there is at least one token
+ * in the string after the current position; false otherwise.
+ */
+ public boolean hasMoreTokens() {
+ if (lookahead != null) {
+ return true;
+ }
+
+ return tokenizer.hasMoreTokens();
+ }
+
+ /**
+ * Returns the next path element from this tokenizer.
+ *
+ * @return the next path element from this tokenizer.
+ *
+ * @exception NoSuchElementException if there are no more elements in this
+ * tokenizer's path.
+ */
+ public String nextToken() throws NoSuchElementException {
+ String token = null;
+ if (lookahead != null) {
+ token = lookahead;
+ lookahead = null;
+ } else {
+ token = tokenizer.nextToken().trim();
+ }
+
+ if (!onNetWare) {
+ if (token.length() == 1 && Character.isLetter(token.charAt(0))
+ && dosStyleFilesystem
+ && tokenizer.hasMoreTokens()) {
+ // we are on a dos style system so this path could be a drive
+ // spec. We look at the next token
+ String nextToken = tokenizer.nextToken().trim();
+ if (nextToken.startsWith("\\") || nextToken.startsWith("/")) {
+ // we know we are on a DOS style platform and the next path
+ // starts with a slash or backslash, so we know this is a
+ // drive spec
+ token += ":" + nextToken;
+ } else {
+ // store the token just read for next time
+ lookahead = nextToken;
+ }
+ }
+ } else {
+ // we are on NetWare, tokenizing is handled a little differently,
+ // due to the fact that NetWare has multiple-character volume names.
+ if (token.equals(File.pathSeparator) || token.equals(":")) {
+ // ignore ";" and get the next token
+ token = tokenizer.nextToken().trim();
+ }
+
+ if (tokenizer.hasMoreTokens()) {
+ // this path could be a drive spec, so look at the next token
+ String nextToken = tokenizer.nextToken().trim();
+
+ // make sure we aren't going to get the path separator next
+ if (!nextToken.equals(File.pathSeparator)) {
+ if (nextToken.equals(":")) {
+ if (!token.startsWith("/") && !token.startsWith("\\")
+ && !token.startsWith(".")
+ && !token.startsWith("..")) {
+ // it indeed is a drive spec, get the next bit
+ String oneMore = tokenizer.nextToken().trim();
+ if (!oneMore.equals(File.pathSeparator)) {
+ token += ":" + oneMore;
+ } else {
+ token += ":";
+ lookahead = oneMore;
+ }
+ }
+ // implicit else: ignore the ':' since we have either a
+ // UNIX or a relative path
+ } else {
+ // store the token just read for next time
+ lookahead = nextToken;
+ }
+ }
+ }
+ }
+ return token;
+ }
+}
+
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java
new file mode 100644
index 000000000..7b57e4956
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant;
+
+/**
+ * Base class for components of a project, including tasks and data types. Provides common facilities.
+ *
+ */
+public abstract class ProjectComponent implements Cloneable {
+
+// // CheckStyle:VisibilityModifier OFF - bc
+// /**
+// * Project object of this component.
+// * @deprecated since 1.6.x.
+// * You should not be directly accessing this variable directly.
+// * You should access project object via the getProject()
+// * or setProject() accessor/mutators.
+// */
+// protected Project project;
+ /**
+ * Location within the build file of this task definition.
+ *
+ * @deprecated since 1.6.x. You should not be accessing this variable directly. Please use the
+ * {@link #getLocation()} method.
+ */
+ protected Location location = Location.UNKNOWN_LOCATION;
+
+ /**
+ * Description of this component, if any.
+ *
+ * @deprecated since 1.6.x. You should not be accessing this variable directly.
+ */
+ protected String description;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Sole constructor.
+ */
+ public ProjectComponent() {
+ }
+
+// /**
+// * Sets the project object of this component. This method is used by
+// * Project when a component is added to it so that the component has
+// * access to the functions of the project. It should not be used
+// * for any other purpose.
+// *
+// * @param project Project in whose scope this component belongs.
+// * Must not be null.
+// */
+// public void setProject(Project project) {
+// this.project = project;
+// }
+//
+// /**
+// * Returns the project to which this component belongs.
+// *
+// * @return the components's project.
+// */
+// public Project getProject() {
+// return project;
+// }
+ /**
+ * Returns the file/location where this task was defined.
+ *
+ * @return the file/location where this task was defined. Should not return null.
+ * Location.UNKNOWN_LOCATION is used for unknown locations.
+ *
+ * @see Location#UNKNOWN_LOCATION
+ */
+ public Location getLocation() {
+ return location;
+ }
+
+ /**
+ * Sets the file/location where this task was defined.
+ *
+ * @param location The file/location where this task was defined. Should not be null--use
+ * Location.UNKNOWN_LOCATION if the location isn't known.
+ *
+ * @see Location#UNKNOWN_LOCATION
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ /**
+ * Sets a description of the current action. This may be used for logging purposes.
+ *
+ * @param desc Description of the current action. May be null, indicating that no description is
+ * available.
+ *
+ */
+ public void setDescription(String desc) {
+ description = desc;
+ }
+
+ /**
+ * Returns the description of the current action.
+ *
+ * @return the description of the current action, or null if no description is available.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Logs a message with the default (INFO) priority.
+ *
+ * @param msg The message to be logged. Should not be null.
+ */
+ public void log(String msg) {
+// log(msg, Project.MSG_INFO);
+ }
+
+ /**
+ * Logs a message with the given priority.
+ *
+ * @param msg The message to be logged. Should not be null.
+ * @param msgLevel the message priority at which this message is to be logged.
+ */
+ public void log(String msg, int msgLevel) {
+// if (getProject() != null) {
+// getProject().log(msg, msgLevel);
+// } else {
+// // 'reasonable' default, if the component is used without
+// // a Project ( for example as a standalone Bean ).
+// // Most ant components can be used this way.
+// if (msgLevel <= Project.MSG_INFO) {
+// System.err.println(msg);
+// }
+// }
+ }
+
+ /**
+ * @since Ant 1.7
+ * @return a shallow copy of this projectcomponent.
+ * @throws CloneNotSupportedException does not happen, but is declared to allow subclasses to do so.
+ */
+ public Object clone() throws CloneNotSupportedException {
+ ProjectComponent pc = (ProjectComponent) super.clone();
+ pc.setLocation(getLocation());
+ //pc.setProject(getProject());
+ return pc;
+ }
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java
new file mode 100644
index 000000000..dca690640
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java
@@ -0,0 +1,530 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant.launch;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.Locale;
+
+import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils;
+
+// CheckStyle:LineLengthCheck OFF - urls are long!
+/**
+ * The Locator is a utility class which is used to find certain items
+ * in the environment.
+ *
+ * It is used at boot time in the launcher, and cannot make use of any of Ant's other classes.
+ *
+ * This is a surprisingly brittle piece of code, and has had lots of bugs filed against it.
+ * {@link running ant off a network share can cause Ant to fail}
+ * {@link use File.toURI().toURL().toExternalForm()}
+ * {@link Locator implementation not encoding URI strings properly: spaces in paths}
+ * It also breaks Eclipse 3.3 Betas
+ * {@link Exception if installation path has spaces}
+ *
+ * Be very careful when making changes to this class, as a break will upset a lot of people.
+ * @since Ant 1.6
+ */
+// CheckStyle:LineLengthCheck ON - urls are long!
+public final class Locator {
+
+ private static final int NIBBLE = 4;
+ private static final int NIBBLE_MASK = 0xF;
+
+ private static final int ASCII_SIZE = 128;
+
+ private static final int BYTE_SIZE = 256;
+
+ private static final int WORD = 16;
+
+ private static final int SPACE = 0x20;
+ private static final int DEL = 0x7F;
+
+ /**
+ * encoding used to represent URIs
+ */
+ public static final String URI_ENCODING = "UTF-8";
+ // stolen from org.apache.xerces.impl.XMLEntityManager#getUserDir()
+ // of the Xerces-J team
+ // which ASCII characters need to be escaped
+ private static boolean[] gNeedEscaping = new boolean[ASCII_SIZE];
+ // the first hex character if a character needs to be escaped
+ private static char[] gAfterEscaping1 = new char[ASCII_SIZE];
+ // the second hex character if a character needs to be escaped
+ private static char[] gAfterEscaping2 = new char[ASCII_SIZE];
+ private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ /** Error string used when an invalid uri is seen */
+ public static final String ERROR_NOT_FILE_URI
+ = "Can only handle valid file: URIs, not ";
+
+ // initialize the above 3 arrays
+ static {
+ for (int i = 0; i < SPACE; i++) {
+ gNeedEscaping[i] = true;
+ gAfterEscaping1[i] = gHexChs[i >> NIBBLE];
+ gAfterEscaping2[i] = gHexChs[i & NIBBLE_MASK];
+ }
+ gNeedEscaping[DEL] = true;
+ gAfterEscaping1[DEL] = '7';
+ gAfterEscaping2[DEL] = 'F';
+ char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',
+ '|', '\\', '^', '~', '[', ']', '`'};
+ int len = escChs.length;
+ char ch;
+ for (int i = 0; i < len; i++) {
+ ch = escChs[i];
+ gNeedEscaping[ch] = true;
+ gAfterEscaping1[ch] = gHexChs[ch >> NIBBLE];
+ gAfterEscaping2[ch] = gHexChs[ch & NIBBLE_MASK];
+ }
+ }
+ /**
+ * Not instantiable
+ */
+ private Locator() {
+ }
+
+ /**
+ * Find the directory or jar file the class has been loaded from.
+ *
+ * @param c the class whose location is required.
+ * @return the file or jar with the class or null if we cannot
+ * determine the location.
+ *
+ * @since Ant 1.6
+ */
+ public static File getClassSource(Class> c) {
+ String classResource = c.getName().replace('.', '/') + ".class";
+ return getResourceSource(c.getClassLoader(), classResource);
+ }
+
+ /**
+ * Find the directory or jar a given resource has been loaded from.
+ *
+ * @param c the classloader to be consulted for the source.
+ * @param resource the resource whose location is required.
+ *
+ * @return the file with the resource source or null if
+ * we cannot determine the location.
+ *
+ * @since Ant 1.6
+ */
+ public static File getResourceSource(ClassLoader c, String resource) {
+ if (c == null) {
+ c = Locator.class.getClassLoader();
+ }
+ URL url = null;
+ if (c == null) {
+ url = ClassLoader.getSystemResource(resource);
+ } else {
+ url = c.getResource(resource);
+ }
+ if (url != null) {
+ String u = url.toString();
+ try {
+ if (u.startsWith("jar:file:")) {
+ return new File(fromJarURI(u));
+ } else if (u.startsWith("file:")) {
+ int tail = u.indexOf(resource);
+ String dirName = u.substring(0, tail);
+ return new File(fromURI(dirName));
+ }
+ } catch (IllegalArgumentException e) {
+ //unable to determine the URI for reasons unknown.
+ return null;
+ }
+ }
+ return null;
+ }
+
+
+
+ /**
+ * Constructs a file path from a file: URI.
+ *
+ *
Will be an absolute path if the given URI is absolute.
+ *
+ *
Prior to Java 1.4,
+ * swallows '%' that are not followed by two characters.
+ *
+ * See dt-sysid
+ * which makes some mention of how
+ * characters not supported by URI Reference syntax should be escaped.
+ *
+ * @param uri the URI designating a file in the local filesystem.
+ * @return the local file system path for the file.
+ * @throws IllegalArgumentException if the URI is malformed or not a legal file: URL
+ * @since Ant 1.6
+ */
+ public static String fromURI(String uri) {
+ return fromURIJava13(uri);
+ // #buzilla8031: first try Java 1.4.
+ // TODO should use java.net.URI now that we can rely on 1.4...
+ // but check for UNC-related regressions, e.g. #42275
+ // (and remember that \\server\share\file -> file:////server/share/file
+ // rather than -> file://server/share/file as it should;
+ // fixed only in JDK 7's java.nio.file.Path.toUri)
+ // return fromUriJava14(uri);
+ }
+
+ /**
+ * Java1.4+ code to extract the path from the URI.
+ * @param uri
+ * @return null if a conversion was not possible
+ */
+ /* currently unused:
+ private static String fromUriJava14(String uri) {
+ // Also check for properly formed URIs. Ant formerly recommended using
+ // nonsense URIs such as "file:./foo.xml" in XML includes. You shouldn't
+ // do that (just "foo.xml" is correct) but for compatibility we special-case
+ // things when the path is not absolute, and fall back to the old parsing behavior.
+ if (uri.startsWith("file:/")) {
+ try {
+ File f = new File(URI.create(encodeURI(uri)));
+ //bug #42227 forgot to decode before returning
+ return decodeUri(f.getAbsolutePath());
+ } catch (IllegalArgumentException e) {
+ // Bad URI, pass this on.
+ // no, this is downgraded to a warning after various
+ // JRE bugs surfaced. Hand off
+ // to our built in code on a failure
+ //throw new IllegalArgumentException(
+ // "Bad URI " + uri + ":" + e.getMessage(), e);
+ e.printStackTrace();
+ } catch (Exception e) {
+ // Unexpected exception? Should not happen.
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+ */
+
+ /**
+ * @param uri uri to expand
+ * @return the decoded URI
+ * @since Ant1.7.1
+ */
+ private static String fromURIJava13(String uri) {
+ // Fallback method for Java 1.3 or earlier.
+
+ URL url = null;
+ try {
+ url = new URL(uri);
+ } catch (MalformedURLException emYouEarlEx) {
+ // Ignore malformed exception
+ }
+ if (url == null || !("file".equals(url.getProtocol()))) {
+ throw new IllegalArgumentException(ERROR_NOT_FILE_URI + uri);
+ }
+ StringBuffer buf = new StringBuffer(url.getHost());
+ if (buf.length() > 0) {
+ buf.insert(0, File.separatorChar).insert(0, File.separatorChar);
+ }
+ String file = url.getFile();
+ int queryPos = file.indexOf('?');
+ buf.append((queryPos < 0) ? file : file.substring(0, queryPos));
+
+ uri = buf.toString().replace('/', File.separatorChar);
+
+ if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2
+ && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) {
+ uri = uri.substring(1);
+ }
+ String path = null;
+ try {
+ path = decodeUri(uri);
+ //consider adding the current directory. This is not done when
+ //the path is a UNC name
+ String cwd = System.getProperty("user.dir");
+ int posi = cwd.indexOf(':');
+ boolean pathStartsWithFileSeparator = path.startsWith(File.separator);
+ boolean pathStartsWithUNC = path.startsWith("" + File.separator + File.separator);
+ if ((posi > 0) && pathStartsWithFileSeparator && !pathStartsWithUNC) {
+ path = cwd.substring(0, posi + 1) + path;
+ }
+ } catch (UnsupportedEncodingException exc) {
+ // not sure whether this is clean, but this method is
+ // declared not to throw exceptions.
+ throw new IllegalStateException(
+ "Could not convert URI " + uri + " to path: "
+ + exc.getMessage());
+ }
+ return path;
+ }
+
+ /**
+ * Crack a JAR URI.
+ * This method is public for testing; we may delete it without any warning -it is not part of Ant's stable API.
+ * @param uri uri to expand; contains jar: somewhere in it
+ * @return the decoded URI
+ * @since Ant1.7.1
+ */
+ public static String fromJarURI(String uri) {
+ int pling = uri.indexOf("!/");
+ String jarName = uri.substring("jar:".length(), pling);
+ return fromURI(jarName);
+ }
+
+ /**
+ * Decodes an Uri with % characters.
+ * The URI is escaped
+ * @param uri String with the uri possibly containing % characters.
+ * @return The decoded Uri
+ * @throws UnsupportedEncodingException if UTF-8 is not available
+ * @since Ant 1.7
+ */
+ public static String decodeUri(String uri) throws UnsupportedEncodingException {
+ if (uri.indexOf('%') == -1) {
+ return uri;
+ }
+ ByteArrayOutputStream sb = new ByteArrayOutputStream(uri.length());
+ CharacterIterator iter = new StringCharacterIterator(uri);
+ for (char c = iter.first(); c != CharacterIterator.DONE;
+ c = iter.next()) {
+ if (c == '%') {
+ char c1 = iter.next();
+ if (c1 != CharacterIterator.DONE) {
+ int i1 = Character.digit(c1, WORD);
+ char c2 = iter.next();
+ if (c2 != CharacterIterator.DONE) {
+ int i2 = Character.digit(c2, WORD);
+ sb.write((char) ((i1 << NIBBLE) + i2));
+ }
+ }
+ } else if (c >= 0x0000 && c < 0x0080) {
+ sb.write(c);
+ } else { // #50543
+ byte[] bytes = String.valueOf(c).getBytes(URI_ENCODING);
+ sb.write(bytes, 0, bytes.length);
+ }
+ }
+ return sb.toString(URI_ENCODING);
+ }
+
+ /**
+ * Encodes an Uri with % characters.
+ * The URI is escaped
+ * @param path String to encode.
+ * @return The encoded string, according to URI norms
+ * @throws UnsupportedEncodingException if UTF-8 is not available
+ * @since Ant 1.7
+ */
+ public static String encodeURI(String path) throws UnsupportedEncodingException {
+ int i = 0;
+ int len = path.length();
+ int ch = 0;
+ StringBuffer sb = null;
+ for (; i < len; i++) {
+ ch = path.charAt(i);
+ // if it's not an ASCII character, break here, and use UTF-8 encoding
+ if (ch >= ASCII_SIZE) {
+ break;
+ }
+ if (gNeedEscaping[ch]) {
+ if (sb == null) {
+ sb = new StringBuffer(path.substring(0, i));
+ }
+ sb.append('%');
+ sb.append(gAfterEscaping1[ch]);
+ sb.append(gAfterEscaping2[ch]);
+ // record the fact that it's escaped
+ } else if (sb != null) {
+ sb.append((char) ch);
+ }
+ }
+
+ // we saw some non-ascii character
+ if (i < len) {
+ if (sb == null) {
+ sb = new StringBuffer(path.substring(0, i));
+ }
+ // get UTF-8 bytes for the remaining sub-string
+ byte[] bytes = null;
+ byte b;
+ bytes = path.substring(i).getBytes(URI_ENCODING);
+ len = bytes.length;
+
+ // for each byte
+ for (i = 0; i < len; i++) {
+ b = bytes[i];
+ // for non-ascii character: make it positive, then escape
+ if (b < 0) {
+ ch = b + BYTE_SIZE;
+ sb.append('%');
+ sb.append(gHexChs[ch >> NIBBLE]);
+ sb.append(gHexChs[ch & NIBBLE_MASK]);
+ } else if (gNeedEscaping[b]) {
+ sb.append('%');
+ sb.append(gAfterEscaping1[b]);
+ sb.append(gAfterEscaping2[b]);
+ } else {
+ sb.append((char) b);
+ }
+ }
+ }
+ return sb == null ? path : sb.toString();
+ }
+
+ /**
+ * Convert a File to a URL.
+ * File.toURL() does not encode characters like #.
+ * File.toURI() has been introduced in java 1.4, so
+ * Ant cannot use it (except by reflection)
+ * FileUtils.toURI() cannot be used by Locator.java
+ * Implemented this way.
+ * File.toURL() adds file: and changes '\' to '/' for dos OSes
+ * encodeURI converts characters like ' ' and '#' to %DD
+ * @param file the file to convert
+ * @return URL the converted File
+ * @throws MalformedURLException on error
+ * @deprecated since 1.9, use {@link FileUtils#getFileURL(File)}
+ */
+ @Deprecated
+ public static URL fileToURL(File file)
+ throws MalformedURLException {
+ return new URL(file.toURI().toASCIIString());
+ }
+
+ /**
+ * Get the File necessary to load the Sun compiler tools. If the classes
+ * are available to this class, then no additional URL is required and
+ * null is returned. This may be because the classes are explicitly in the
+ * class path or provided by the JVM directly.
+ *
+ * @return the tools jar as a File if required, null otherwise.
+ */
+ public static File getToolsJar() {
+ // firstly check if the tools jar is already in the classpath
+ boolean toolsJarAvailable = false;
+ try {
+ // just check whether this throws an exception
+ Class.forName("com.sun.tools.javac.Main");
+ toolsJarAvailable = true;
+ } catch (Exception e) {
+ try {
+ Class.forName("sun.tools.javac.Main");
+ toolsJarAvailable = true;
+ } catch (Exception e2) {
+ // ignore
+ }
+ }
+ if (toolsJarAvailable) {
+ return null;
+ }
+ // couldn't find compiler - try to find tools.jar
+ // based on java.home setting
+ String libToolsJar
+ = File.separator + "lib" + File.separator + "tools.jar";
+ String javaHome = System.getProperty("java.home");
+ File toolsJar = new File(javaHome + libToolsJar);
+ if (toolsJar.exists()) {
+ // Found in java.home as given
+ return toolsJar;
+ }
+ if (javaHome.toLowerCase(Locale.ENGLISH).endsWith(File.separator + "jre")) {
+ javaHome = javaHome.substring(
+ 0, javaHome.length() - "/jre".length());
+ toolsJar = new File(javaHome + libToolsJar);
+ }
+ if (!toolsJar.exists()) {
+ System.out.println("Unable to locate tools.jar. "
+ + "Expected to find it in " + toolsJar.getPath());
+ return null;
+ }
+ return toolsJar;
+ }
+
+ /**
+ * Get an array of URLs representing all of the jar files in the
+ * given location. If the location is a file, it is returned as the only
+ * element of the array. If the location is a directory, it is scanned for
+ * jar files.
+ *
+ * @param location the location to scan for Jars.
+ *
+ * @return an array of URLs for all jars in the given location.
+ *
+ * @exception MalformedURLException if the URLs for the jars cannot be
+ * formed.
+ */
+ public static URL[] getLocationURLs(File location)
+ throws MalformedURLException {
+ return getLocationURLs(location, new String[]{".jar"});
+ }
+
+ /**
+ * Get an array of URLs representing all of the files of a given set of
+ * extensions in the given location. If the location is a file, it is
+ * returned as the only element of the array. If the location is a
+ * directory, it is scanned for matching files.
+ *
+ * @param location the location to scan for files.
+ * @param extensions an array of extension that are to match in the
+ * directory search.
+ *
+ * @return an array of URLs of matching files.
+ * @exception MalformedURLException if the URLs for the files cannot be
+ * formed.
+ */
+ public static URL[] getLocationURLs(File location,
+ final String[] extensions)
+ throws MalformedURLException {
+ URL[] urls = new URL[0];
+
+ if (!location.exists()) {
+ return urls;
+ }
+ if (!location.isDirectory()) {
+ urls = new URL[1];
+ String path = location.getPath();
+ String littlePath = path.toLowerCase(Locale.ENGLISH);
+ for (int i = 0; i < extensions.length; ++i) {
+ if (littlePath.endsWith(extensions[i])) {
+ urls[0] = fileToURL(location);
+ break;
+ }
+ }
+ return urls;
+ }
+ File[] matches = location.listFiles(
+ new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ String littleName = name.toLowerCase(Locale.ENGLISH);
+ for (int i = 0; i < extensions.length; ++i) {
+ if (littleName.endsWith(extensions[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+ urls = new URL[matches.length];
+ for (int i = 0; i < matches.length; ++i) {
+ urls[i] = fileToURL(matches[i]);
+ }
+ return urls;
+ }
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java
new file mode 100644
index 000000000..115f3b29e
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java
@@ -0,0 +1,14 @@
+/**
+ *
+ *
+ * org.owasp.dependencycheck.org.apache.tools.ant.launch
+ *
+ *
+ * This is a copy of classes within Apache Ant. The DirectoryScanner
+ * is needed by dependency-check. However, we did not want to make
+ * Ant a dependency. As such, a few files were copied and slightly
+ * modified to remove any references to the Ant Project class.
+ *
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant.launch;
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java
new file mode 100644
index 000000000..cb3c12962
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java
@@ -0,0 +1,14 @@
+/**
+ *
+ *
+ * org.owasp.dependencycheck.org.apache.tools.ant
+ *
+ *
+ * This is a copy of classes within Apache Ant. The DirectoryScanner
+ * is needed by dependency-check. However, we did not want to make
+ * Ant a dependency. As such, a few files were copied and slightly
+ * modified to remove any references to the Ant Project class.
+ *
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant;
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java
new file mode 100644
index 000000000..d6e0351d3
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition;
+
+import org.owasp.dependencycheck.org.apache.tools.ant.BuildException;
+
+/**
+ * Interface for conditions to use inside the <condition> task.
+ *
+ */
+public interface Condition {
+ /**
+ * Is this condition true?
+ * @return true if the condition is true
+ * @exception BuildException if an error occurs
+ */
+ boolean eval() throws BuildException;
+}
+
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java
new file mode 100644
index 000000000..4ea20554e
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition;
+
+import java.util.Locale;
+
+import org.owasp.dependencycheck.org.apache.tools.ant.BuildException;
+
+/**
+ * Condition that tests the OS type.
+ *
+ * @since Ant 1.4
+ */
+public class Os implements Condition {
+ private static final String OS_NAME =
+ System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+ private static final String OS_ARCH =
+ System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
+ private static final String OS_VERSION =
+ System.getProperty("os.version").toLowerCase(Locale.ENGLISH);
+ private static final String PATH_SEP =
+ System.getProperty("path.separator");
+
+ /**
+ * OS family to look for
+ */
+ private String family;
+ /**
+ * Name of OS
+ */
+ private String name;
+ /**
+ * version of OS
+ */
+ private String version;
+ /**
+ * OS architecture
+ */
+ private String arch;
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_WINDOWS = "windows";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_9X = "win9x";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_NT = "winnt";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_OS2 = "os/2";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_NETWARE = "netware";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_DOS = "dos";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_MAC = "mac";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_TANDEM = "tandem";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_UNIX = "unix";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_VMS = "openvms";
+ /**
+ * OS family that can be tested for. {@value}
+ */
+ public static final String FAMILY_ZOS = "z/os";
+ /** OS family that can be tested for. {@value} */
+ public static final String FAMILY_OS400 = "os/400";
+
+ /**
+ * OpenJDK is reported to call MacOS X "Darwin"
+ * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889
+ * @see https://issues.apache.org/jira/browse/HADOOP-3318
+ */
+ private static final String DARWIN = "darwin";
+
+ /**
+ * Default constructor
+ *
+ */
+ public Os() {
+ //default
+ }
+
+ /**
+ * Constructor that sets the family attribute
+ * @param family a String value
+ */
+ public Os(String family) {
+ setFamily(family);
+ }
+
+ /**
+ * Sets the desired OS family type
+ *
+ * @param f The OS family type desired
+ * Possible values:
+ *
+ *
dos
+ *
mac
+ *
netware
+ *
os/2
+ *
tandem
+ *
unix
+ *
windows
+ *
win9x
+ *
z/os
+ *
os/400
+ *
+ */
+ public void setFamily(String f) {
+ family = f.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Sets the desired OS name
+ *
+ * @param name The OS name
+ */
+ public void setName(String name) {
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Sets the desired OS architecture
+ *
+ * @param arch The OS architecture
+ */
+ public void setArch(String arch) {
+ this.arch = arch.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Sets the desired OS version
+ *
+ * @param version The OS version
+ */
+ public void setVersion(String version) {
+ this.version = version.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the type of
+ * that set in setFamily.
+ * @return true if the os matches.
+ * @throws BuildException if there is an error.
+ * @see Os#setFamily(String)
+ */
+ public boolean eval() throws BuildException {
+ return isOs(family, name, arch, version);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS family.
+ * @param family the family to check for
+ * @return true if the OS matches
+ * @since 1.5
+ */
+ public static boolean isFamily(String family) {
+ return isOs(family, null, null, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS name.
+ *
+ * @param name the OS name to check for
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isName(String name) {
+ return isOs(null, name, null, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS architecture.
+ *
+ * @param arch the OS architecture to check for
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isArch(String arch) {
+ return isOs(null, null, arch, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS version.
+ *
+ * @param version the OS version to check for
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isVersion(String version) {
+ return isOs(null, null, null, version);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the
+ * given OS family, name, architecture and version
+ *
+ * @param family The OS family
+ * @param name The OS name
+ * @param arch The OS architecture
+ * @param version The OS version
+ * @return true if the OS matches
+ * @since 1.7
+ */
+ public static boolean isOs(String family, String name, String arch,
+ String version) {
+ boolean retValue = false;
+
+ if (family != null || name != null || arch != null
+ || version != null) {
+
+ boolean isFamily = true;
+ boolean isName = true;
+ boolean isArch = true;
+ boolean isVersion = true;
+
+ if (family != null) {
+
+ //windows probing logic relies on the word 'windows' in
+ //the OS
+ boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1;
+ boolean is9x = false;
+ boolean isNT = false;
+ if (isWindows) {
+ //there are only four 9x platforms that we look for
+ is9x = (OS_NAME.indexOf("95") >= 0
+ || OS_NAME.indexOf("98") >= 0
+ || OS_NAME.indexOf("me") >= 0
+ //wince isn't really 9x, but crippled enough to
+ //be a muchness. Ant doesnt run on CE, anyway.
+ || OS_NAME.indexOf("ce") >= 0);
+ isNT = !is9x;
+ }
+ if (family.equals(FAMILY_WINDOWS)) {
+ isFamily = isWindows;
+ } else if (family.equals(FAMILY_9X)) {
+ isFamily = isWindows && is9x;
+ } else if (family.equals(FAMILY_NT)) {
+ isFamily = isWindows && isNT;
+ } else if (family.equals(FAMILY_OS2)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1;
+ } else if (family.equals(FAMILY_NETWARE)) {
+ isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1;
+ } else if (family.equals(FAMILY_DOS)) {
+ isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE);
+ } else if (family.equals(FAMILY_MAC)) {
+ isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1
+ || OS_NAME.indexOf(DARWIN) > -1;
+ } else if (family.equals(FAMILY_TANDEM)) {
+ isFamily = OS_NAME.indexOf("nonstop_kernel") > -1;
+ } else if (family.equals(FAMILY_UNIX)) {
+ isFamily = PATH_SEP.equals(":")
+ && !isFamily(FAMILY_VMS)
+ && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x")
+ || OS_NAME.indexOf(DARWIN) > -1);
+ } else if (family.equals(FAMILY_ZOS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1
+ || OS_NAME.indexOf("os/390") > -1;
+ } else if (family.equals(FAMILY_OS400)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1;
+ } else if (family.equals(FAMILY_VMS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1;
+ } else {
+ throw new BuildException(
+ "Don\'t know how to detect os family \""
+ + family + "\"");
+ }
+ }
+ if (name != null) {
+ isName = name.equals(OS_NAME);
+ }
+ if (arch != null) {
+ isArch = arch.equals(OS_ARCH);
+ }
+ if (version != null) {
+ isVersion = version.equals(OS_VERSION);
+ }
+ retValue = isFamily && isName && isArch && isVersion;
+ }
+ return retValue;
+ }
+}
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java
new file mode 100644
index 000000000..58f73450b
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java
@@ -0,0 +1,14 @@
+/**
+ *
+ *
+ * org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition
+ *
+ *
+ * This is a copy of classes within Apache Ant. The DirectoryScanner
+ * is needed by dependency-check. However, we did not want to make
+ * Ant a dependency. As such, a few files were copied and slightly
+ * modified to remove any references to the Ant Project class.
+ *
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition;
diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java
new file mode 100644
index 000000000..d97367c90
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.owasp.dependencycheck.org.apache.tools.ant.types;
+
+import org.owasp.dependencycheck.org.apache.tools.ant.BuildException;
+import org.owasp.dependencycheck.org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Base class for those classes that can appear inside the build file as stand alone data types.
+ *
+ *
+ * This class handles the common description attribute and provides a default implementation for reference handling and
+ * checking for circular references that is appropriate for types that can not be nested inside elements of the same
+ * type (i.e. <patternset> but not <path>).
+ *
+ */
+public abstract class DataType extends ProjectComponent implements Cloneable {
+ // CheckStyle:VisibilityModifier OFF
+
+ /**
+ * Value to the refid attribute.
+ *
+ * @deprecated since 1.7. The user should not be directly referencing variable. Please use {@link #getRefid}
+ * instead.
+ */
+ protected Reference ref;
+
+ /**
+ * Are we sure we don't hold circular references?
+ *
+ *
+ * Subclasses are responsible for setting this value to false if we'd need to investigate this condition (usually
+ * because a child element has been added that is a subclass of DataType).
+ *
+ * @deprecated since 1.7. The user should not be directly referencing variable. Please use {@link #setChecked} or
+ * {@link #isChecked} instead.
+ */
+ protected boolean checked = true;
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Has the refid attribute of this element been set?
+ *
+ * @return true if the refid attribute has been set
+ */
+ public boolean isReference() {
+ return ref != null;
+ }
+
+ /**
+ * Set the value of the refid attribute.
+ *
+ *
+ * Subclasses may need to check whether any other attributes have been set as well or child elements have been
+ * created and thus override this method. if they do the must call super.setRefid.
+ *
+ * @param ref the reference to use
+ */
+ public void setRefid(final Reference ref) {
+ this.ref = ref;
+ checked = false;
+ }
+
+// /**
+// * Gets as descriptive as possible a name used for this datatype instance.
+// *
+// * @return String name.
+// */
+// protected String getDataTypeName() {
+// return ComponentHelper.getElementName(getProject(), this, true);
+// }
+// /**
+// * Convenience method.
+// * @since Ant 1.7
+// */
+// protected void dieOnCircularReference() {
+// dieOnCircularReference(getProject());
+// }
+//
+// /**
+// * Convenience method.
+// * @param p the Ant Project instance against which to resolve references.
+// * @since Ant 1.7
+// */
+// protected void dieOnCircularReference(Project p) {
+// if (checked || !isReference()) {
+// return;
+// }
+// dieOnCircularReference(new IdentityStack