Use NIO to simplify and cleanup path processing
diff --git a/src/main/java/org/apache/commons/io/FileSystemUtils.java b/src/main/java/org/apache/commons/io/FileSystemUtils.java
index 1284285..ec8a4ea 100644
--- a/src/main/java/org/apache/commons/io/FileSystemUtils.java
+++ b/src/main/java/org/apache/commons/io/FileSystemUtils.java
@@ -22,6 +22,8 @@
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
@@ -33,8 +35,7 @@
/**
* General File System utilities.
* <p>
- * This class provides static utility methods for general file system
- * functions not provided via the JDK {@link java.io.File File} class.
+ * This class provides static utility methods for general file system functions not provided via the JDK {@link java.io.File File} class.
* <p>
* The current functions provided are:
* <ul>
@@ -42,10 +43,8 @@
* </ul>
*
* @since 1.1
- * @deprecated As of 2.6 deprecated without replacement. Use equivalent
- * methods in {@link java.nio.file.FileStore} instead, e.g.
- * {@code Files.getFileStore(Paths.get("/home")).getUsableSpace()}
- * or iterate over {@code FileSystems.getDefault().getFileStores()}
+ * @deprecated As of 2.6 deprecated without replacement. Use equivalent methods in {@link java.nio.file.FileStore} instead, e.g.
+ * {@code Files.getFileStore(Paths.get("/home")).getUsableSpace()} or iterate over {@code FileSystems.getDefault().getFileStores()}
*/
@Deprecated
public class FileSystemUtils {
@@ -82,22 +81,13 @@
// match
if (osName.contains("windows")) {
os = WINDOWS;
- } else if (osName.contains("linux") ||
- osName.contains("mpe/ix") ||
- osName.contains("freebsd") ||
- osName.contains("openbsd") ||
- osName.contains("irix") ||
- osName.contains("digital unix") ||
- osName.contains("unix") ||
- osName.contains("mac os x")) {
+ } else if (osName.contains("linux") || osName.contains("mpe/ix") || osName.contains("freebsd") || osName.contains("openbsd")
+ || osName.contains("irix") || osName.contains("digital unix") || osName.contains("unix") || osName.contains("mac os x")) {
os = UNIX;
- } else if (osName.contains("sun os") ||
- osName.contains("sunos") ||
- osName.contains("solaris")) {
+ } else if (osName.contains("sun os") || osName.contains("sunos") || osName.contains("solaris")) {
os = POSIX_UNIX;
dfPath = "/usr/xpg4/bin/df";
- } else if (osName.contains("hp-ux") ||
- osName.contains("aix")) {
+ } else if (osName.contains("hp-ux") || osName.contains("aix")) {
os = POSIX_UNIX;
}
@@ -109,30 +99,26 @@
}
/**
- * Returns the free space on a drive or volume by invoking
- * the command line.
- * This method does not normalize the result, and typically returns
- * bytes on Windows, 512 byte units on OS X and kilobytes on Unix.
- * As this is not very useful, this method is deprecated in favor
- * of {@link #freeSpaceKb(String)} which returns a result in kilobytes.
+ * Returns the free space on a drive or volume by invoking the command line. This method does not normalize the result, and typically returns bytes on
+ * Windows, 512 byte units on OS X and kilobytes on Unix. As this is not very useful, this method is deprecated in favor of {@link #freeSpaceKb(String)}
+ * which returns a result in kilobytes.
* <p>
- * Note that some OS's are NOT currently supported, including OS/390,
- * OpenVMS.
- * <pre>
- * FileSystemUtils.freeSpace("C:"); // Windows
- * FileSystemUtils.freeSpace("/volume"); // *nix
- * </pre>
- * The free space is calculated via the command line.
- * It uses 'dir /-c' on Windows and 'df' on *nix.
+ * Note that some OS's are NOT currently supported, including OS/390, OpenVMS.
*
- * @param path the path to get free space for, not null, not empty on Unix
+ * <pre>
+ * FileSystemUtils.freeSpace("C:"); // Windows
+ * FileSystemUtils.freeSpace("/volume"); // *nix
+ * </pre>
+ *
+ * The free space is calculated via the command line. It uses 'dir /-c' on Windows and 'df' on *nix.
+ *
+ * @param path the path to get free space for, not null, not empty on Unix
* @return the amount of free drive space on the drive or volume
* @throws IllegalArgumentException if the path is invalid
- * @throws IllegalStateException if an error occurred in initialization
- * @throws IOException if an error occurs when finding the free space
+ * @throws IllegalStateException if an error occurred in initialization
+ * @throws IOException if an error occurs when finding the free space
* @since 1.1, enhanced OS support in 1.2 and 1.3
- * @deprecated Use freeSpaceKb(String)
- * Deprecated from 1.3, may be removed in 2.0
+ * @deprecated Use freeSpaceKb(String) Deprecated from 1.3, may be removed in 2.0
*/
@Deprecated
public static long freeSpace(final String path) throws IOException {
@@ -140,16 +126,17 @@
}
/**
- * Returns the free space for the working directory
- * in kibibytes (1024 bytes) by invoking the command line.
+ * Returns the free space for the working directory in kibibytes (1024 bytes) by invoking the command line.
* <p>
* Identical to:
+ *
* <pre>
* freeSpaceKb(FileUtils.current().getAbsolutePath())
* </pre>
+ *
* @return the amount of free drive space on the drive or volume in kilobytes
* @throws IllegalStateException if an error occurred in initialization
- * @throws IOException if an error occurs when finding the free space
+ * @throws IOException if an error occurs when finding the free space
* @since 2.0
* @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
*/
@@ -159,18 +146,18 @@
}
/**
- * Returns the free space for the working directory
- * in kibibytes (1024 bytes) by invoking the command line.
+ * Returns the free space for the working directory in kibibytes (1024 bytes) by invoking the command line.
* <p>
* Identical to:
+ *
* <pre>
* freeSpaceKb(FileUtils.current().getAbsolutePath())
* </pre>
- * @param timeout The timeout amount in milliseconds or no timeout if the value
- * is zero or less
+ *
+ * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less
* @return the amount of free drive space on the drive or volume in kilobytes
* @throws IllegalStateException if an error occurred in initialization
- * @throws IOException if an error occurs when finding the free space
+ * @throws IOException if an error occurs when finding the free space
* @since 2.0
* @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
*/
@@ -178,28 +165,26 @@
public static long freeSpaceKb(final long timeout) throws IOException {
return freeSpaceKb(FileUtils.current().getAbsolutePath(), timeout);
}
+
/**
- * Returns the free space on a drive or volume in kibibytes (1024 bytes)
- * by invoking the command line.
- * <pre>
- * FileSystemUtils.freeSpaceKb("C:"); // Windows
- * FileSystemUtils.freeSpaceKb("/volume"); // *nix
- * </pre>
- * The free space is calculated via the command line.
- * It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
- * <p>
- * In order to work, you must be running Windows, or have an implementation of
- * Unix df that supports GNU format when passed -k (or -kP). If you are going
- * to rely on this code, please check that it works on your OS by running
- * some simple tests to compare the command line with the output from this class.
- * If your operating system isn't supported, please raise a JIRA call detailing
- * the exact result from df -k and as much other detail as possible, thanks.
+ * Returns the free space on a drive or volume in kibibytes (1024 bytes) by invoking the command line.
*
- * @param path the path to get free space for, not null, not empty on Unix
+ * <pre>
+ * FileSystemUtils.freeSpaceKb("C:"); // Windows
+ * FileSystemUtils.freeSpaceKb("/volume"); // *nix
+ * </pre>
+ *
+ * The free space is calculated via the command line. It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
+ * <p>
+ * In order to work, you must be running Windows, or have an implementation of Unix df that supports GNU format when passed -k (or -kP). If you are going to
+ * rely on this code, please check that it works on your OS by running some simple tests to compare the command line with the output from this class. If
+ * your operating system isn't supported, please raise a JIRA call detailing the exact result from df -k and as much other detail as possible, thanks.
+ *
+ * @param path the path to get free space for, not null, not empty on Unix
* @return the amount of free drive space on the drive or volume in kilobytes
* @throws IllegalArgumentException if the path is invalid
- * @throws IllegalStateException if an error occurred in initialization
- * @throws IOException if an error occurs when finding the free space
+ * @throws IllegalStateException if an error occurred in initialization
+ * @throws IOException if an error occurs when finding the free space
* @since 1.2, enhanced OS support in 1.3
* @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
*/
@@ -209,29 +194,25 @@
}
/**
- * Returns the free space on a drive or volume in kibibytes (1024 bytes)
- * by invoking the command line.
- * <pre>
- * FileSystemUtils.freeSpaceKb("C:"); // Windows
- * FileSystemUtils.freeSpaceKb("/volume"); // *nix
- * </pre>
- * The free space is calculated via the command line.
- * It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
- * <p>
- * In order to work, you must be running Windows, or have an implementation of
- * Unix df that supports GNU format when passed -k (or -kP). If you are going
- * to rely on this code, please check that it works on your OS by running
- * some simple tests to compare the command line with the output from this class.
- * If your operating system isn't supported, please raise a JIRA call detailing
- * the exact result from df -k and as much other detail as possible, thanks.
+ * Returns the free space on a drive or volume in kibibytes (1024 bytes) by invoking the command line.
*
- * @param path the path to get free space for, not null, not empty on Unix
- * @param timeout The timeout amount in milliseconds or no timeout if the value
- * is zero or less
+ * <pre>
+ * FileSystemUtils.freeSpaceKb("C:"); // Windows
+ * FileSystemUtils.freeSpaceKb("/volume"); // *nix
+ * </pre>
+ *
+ * The free space is calculated via the command line. It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
+ * <p>
+ * In order to work, you must be running Windows, or have an implementation of Unix df that supports GNU format when passed -k (or -kP). If you are going to
+ * rely on this code, please check that it works on your OS by running some simple tests to compare the command line with the output from this class. If
+ * your operating system isn't supported, please raise a JIRA call detailing the exact result from df -k and as much other detail as possible, thanks.
+ *
+ * @param path the path to get free space for, not null, not empty on Unix
+ * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less
* @return the amount of free drive space on the drive or volume in kilobytes
* @throws IllegalArgumentException if the path is invalid
- * @throws IllegalStateException if an error occurred in initialization
- * @throws IOException if an error occurs when finding the free space
+ * @throws IllegalStateException if an error occurred in initialization
+ * @throws IOException if an error occurs when finding the free space
* @since 2.0
* @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
*/
@@ -251,24 +232,23 @@
}
/**
- * Returns the free space on a drive or volume in a cross-platform manner.
- * Note that some OS's are NOT currently supported, including OS/390.
- * <pre>
- * FileSystemUtils.freeSpace("C:"); // Windows
- * FileSystemUtils.freeSpace("/volume"); // *nix
- * </pre>
- * The free space is calculated via the command line.
- * It uses 'dir /-c' on Windows and 'df' on *nix.
+ * Returns the free space on a drive or volume in a cross-platform manner. Note that some OS's are NOT currently supported, including OS/390.
*
- * @param path the path to get free space for, not null, not empty on Unix
- * @param os the operating system code
- * @param kb whether to normalize to kilobytes
- * @param timeout The timeout amount in milliseconds or no timeout if the value
- * is zero or less
+ * <pre>
+ * FileSystemUtils.freeSpace("C:"); // Windows
+ * FileSystemUtils.freeSpace("/volume"); // *nix
+ * </pre>
+ *
+ * The free space is calculated via the command line. It uses 'dir /-c' on Windows and 'df' on *nix.
+ *
+ * @param path the path to get free space for, not null, not empty on Unix
+ * @param os the operating system code
+ * @param kb whether to normalize to kilobytes
+ * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less
* @return the amount of free drive space on the drive or volume
* @throws IllegalArgumentException if the path is invalid
- * @throws IllegalStateException if an error occurred in initialization
- * @throws IOException if an error occurs when finding the free space
+ * @throws IllegalStateException if an error occurred in initialization
+ * @throws IOException if an error occurs when finding the free space
*/
long freeSpaceOS(final String path, final int os, final boolean kb, final Duration timeout) throws IOException {
Objects.requireNonNull(path, "path");
@@ -289,16 +269,14 @@
/**
* Find free space on the *nix platform using the 'df' command.
*
- * @param path the path to get free space for
- * @param kb whether to normalize to kilobytes
- * @param posix whether to use the POSIX standard format flag
- * @param timeout The timeout amount in milliseconds or no timeout if the value
- * is zero or less
+ * @param path the path to get free space for
+ * @param kb whether to normalize to kilobytes
+ * @param posix whether to use the POSIX standard format flag
+ * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less
* @return the amount of free drive space on the volume
* @throws IOException If an I/O error occurs
*/
- long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout)
- throws IOException {
+ long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout) throws IOException {
if (path.isEmpty()) {
throw new IllegalArgumentException("Path must not be empty");
}
@@ -342,23 +320,29 @@
/**
* Find free space on the Windows platform using the 'dir' command.
*
- * @param path the path to get free space for, including the colon
- * @param timeout The timeout amount in milliseconds or no timeout if the value
- * is zero or less
+ * @param pathStr the path to get free space for, including the colon
+ * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less
* @return the amount of free drive space on the drive
* @throws IOException If an I/O error occurs
*/
- long freeSpaceWindows(final String path, final Duration timeout) throws IOException {
- String normPath = FilenameUtils.normalize(path, false);
- if (normPath == null) {
- throw new IllegalArgumentException(path);
- }
- if (!normPath.isEmpty() && normPath.charAt(0) != '"') {
- normPath = "\"" + normPath + "\"";
+ long freeSpaceWindows(final String pathStr, final Duration timeout) throws IOException {
+ Objects.requireNonNull(pathStr, "path");
+ final String trimPathStr = pathStr.trim();
+ final Path normPath;
+ if (!trimPathStr.isEmpty()) {
+ if (trimPathStr.charAt(0) != '"') {
+ // Paths.get throws IAE if the path is bad before we pass it to a shell.
+ normPath = Paths.get(trimPathStr).normalize();
+ } else {
+ // Paths.get throws IAE if the path is bad before we pass it to a shell.
+ normPath = Paths.get(trimPathStr.substring(1, trimPathStr.length() - 1)).normalize();
+ }
+ } else {
+ normPath = Paths.get(trimPathStr).normalize();
}
// build and run the 'dir' command
- final String[] cmdAttribs = { "cmd.exe", "/C", "dir /a /-c " + normPath };
+ final String[] cmdAttribs = { "cmd.exe", "/C", "dir /a /-c \"" + normPath + "\""};
// read in the output of the command to an ArrayList
final List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE, timeout);
@@ -370,17 +354,17 @@
for (int i = lines.size() - 1; i >= 0; i--) {
final String line = lines.get(i);
if (!line.isEmpty()) {
- return parseDir(line, normPath);
+ return parseDir(line, trimPathStr);
}
}
// all lines are blank
- throw new IOException("Command line 'dir /-c' did not return any info for path '" + normPath + "'");
+ throw new IOException("Command line 'dir /-c' did not return any info for path '" + trimPathStr + "'");
}
/**
* Opens the process to the operating system.
*
- * @param cmdAttribs the command line parameters
+ * @param cmdAttribs the command line parameters
* @return the process
* @throws IOException If an I/O error occurs
*/
@@ -391,8 +375,8 @@
/**
* Parses the bytes from a string.
*
- * @param freeSpace the free space string
- * @param path the path
+ * @param freeSpace the free space string
+ * @param path the path
* @return the number of bytes
* @throws IOException If an I/O error occurs
*/
@@ -412,8 +396,8 @@
/**
* Parses the Windows dir response last line
*
- * @param line the line to parse
- * @param path the path that was sent
+ * @param line the line to parse
+ * @param path the path that was sent
* @return the number of bytes
* @throws IOException If an I/O error occurs
*/
@@ -462,10 +446,9 @@
/**
* Performs an OS command.
*
- * @param cmdAttribs the command line parameters
- * @param max The maximum limit for the lines returned
- * @param timeout The timeout amount in milliseconds or no timeout if the value
- * is zero or less
+ * @param cmdAttribs the command line parameters
+ * @param max The maximum limit for the lines returned
+ * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less
* @return the lines returned by the command, converted to lower-case
* @throws IOException if an error occurs
*/
diff --git a/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java b/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java
index 617f03c..b6af69a 100644
--- a/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java
@@ -31,6 +31,8 @@
import java.util.Locale;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
/**
* Tests {@link FileSystemUtils}.
@@ -93,6 +95,10 @@
private static final Duration NEG_1_TIMEOUT = Duration.ofMillis(-1);
+ static char[] getIllegalFileNameChars() {
+ return FileSystem.getCurrent().getIllegalFileNameChars();
+ }
+
@Test
public void testGetFreeSpace_String() throws Exception {
// test coverage, as we can't check value
@@ -332,6 +338,17 @@
assertEquals(189416L, fsu.freeSpaceUnix("/", false, false, NEG_1_TIMEOUT));
}
+ @ParameterizedTest
+ @MethodSource("getIllegalFileNameChars")
+ public void testGetFreeSpaceWindows_IllegalFileName(final char illegalFileNameChar) throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> new FileSystemUtils().freeSpaceWindows("\\ \"" + illegalFileNameChar, NEG_1_TIMEOUT));
+ }
+
+ @Test
+ public void testGetFreeSpaceWindows_IllegalFileNames() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> new FileSystemUtils().freeSpaceWindows("\\ \"", NEG_1_TIMEOUT));
+ }
+
@Test
public void testGetFreeSpaceWindows_String_EmptyMultiLineResponse() {
final String lines = "\n\n";
@@ -353,7 +370,7 @@
"17/08/2005 21:44 <DIR> Desktop\n" +
" 7 File(s) 180260 bytes\n" +
" 10 Dir(s) 41411551232 bytes free";
- final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c ");
+ final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c \"\"");
assertEquals(41411551232L, fsu.freeSpaceWindows("", NEG_1_TIMEOUT));
}
@@ -370,7 +387,6 @@
final FileSystemUtils fsu = new MockFileSystemUtils(0, lines);
assertThrows(IOException.class, () -> fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT));
}
-
@Test
public void testGetFreeSpaceWindows_String_NormalResponse() throws Exception {
final String lines =
@@ -388,6 +404,7 @@
final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c \"C:\"");
assertEquals(41411551232L, fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT));
}
+
@Test
public void testGetFreeSpaceWindows_String_NoSuchDirectoryResponse() {
final String lines =