1616package org .apache .commons .io ;
1717
1818import java .io .BufferedReader ;
19- import java .io .InputStreamReader ;
2019import java .io .IOException ;
20+ import java .io .InputStreamReader ;
2121import java .util .ArrayList ;
22+ import java .util .Arrays ;
23+ import java .util .List ;
2224import java .util .StringTokenizer ;
2325
2426/**
3739 * @author Thomas Ledoux
3840 * @author James Urie
3941 * @author Magnus Grimsell
42+ * @author Thomas Ledoux
4043 * @version $Id$
4144 * @since Commons IO 1.1
4245 */
@@ -189,6 +192,7 @@ long freeSpaceOS(String path, int os, boolean kb) throws IOException {
189192 }
190193 }
191194
195+ //-----------------------------------------------------------------------
192196 /**
193197 * Find free space on the Windows platform using the 'dir' command.
194198 *
@@ -201,85 +205,82 @@ long freeSpaceWindows(String path) throws IOException {
201205 if (path .length () > 2 && path .charAt (1 ) == ':' ) {
202206 path = path .substring (0 , 2 ); // seems to make it work
203207 }
204-
208+
205209 // build and run the 'dir' command
206- String [] cmdAttrbs = new String [] {"cmd.exe" , "/C" , "dir /-c " + path };
207-
210+ String [] cmdAttribs = new String [] {"cmd.exe" , "/C" , "dir /-c " + path };
211+
208212 // read in the output of the command to an ArrayList
209- BufferedReader in = null ;
210- String line = null ;
211- ArrayList lines = new ArrayList ();
212- try {
213- in = openProcessStream (cmdAttrbs );
214- line = in .readLine ();
215- while (line != null ) {
216- line = line .toLowerCase ().trim ();
217- lines .add (line );
218- line = in .readLine ();
219- }
220- } finally {
221- IOUtils .closeQuietly (in );
222- }
223-
224- if (lines .size () == 0 ) {
225- // unknown problem, throw exception
226- throw new IOException (
227- "Command line 'dir /c' did not return any info " +
228- "for command '" + cmdAttrbs [2 ] + "'" );
229- }
230-
213+ List lines = performCommand (cmdAttribs , Integer .MAX_VALUE );
214+
231215 // now iterate over the lines we just read and find the LAST
232216 // non-empty line (the free space bytes should be in the last element
233217 // of the ArrayList anyway, but this will ensure it works even if it's
234218 // not, still assuming it is on the last non-blank line)
235- long bytes = -1 ;
236- int i = lines .size () - 1 ;
237- int bytesStart = 0 ;
238- int bytesEnd = 0 ;
239- outerLoop : while (i > 0 ) {
240- line = (String ) lines .get (i );
219+ for (int i = lines .size () - 1 ; i >= 0 ; i --) {
220+ String line = (String ) lines .get (i );
241221 if (line .length () > 0 ) {
242- // found it, so now read from the end of the line to find the
243- // last numeric character on the line, then continue until we
244- // find the first non-numeric character, and everything between
245- // that and the last numeric character inclusive is our free
246- // space bytes count
247- int j = line .length () - 1 ;
248- innerLoop1 : while (j >= 0 ) {
249- char c = line .charAt (j );
250- if (Character .isDigit (c )) {
251- // found the last numeric character, this is the end of
252- // the free space bytes count
253- bytesEnd = j + 1 ;
254- break innerLoop1 ;
255- }
256- j --;
257- }
258- innerLoop2 : while (j >= 0 ) {
259- char c = line .charAt (j );
260- if (!Character .isDigit (c ) && c != ',' && c != '.' ) {
261- // found the next non-numeric character, this is the
262- // beginning of the free space bytes count
263- bytesStart = j + 1 ;
264- break innerLoop2 ;
265- }
266- j --;
267- }
268- break outerLoop ;
222+ return parseDir (line , path );
269223 }
270224 }
225+ // all lines are blank
226+ throw new IOException (
227+ "Command line 'dir /-c' did not return any info " +
228+ "for path '" + path + "'" );
229+ }
271230
231+ /**
232+ * Parses the Windows dir response last line
233+ *
234+ * @param line the line to parse
235+ * @param path the path that was sent
236+ * @return the number of bytes
237+ * @throws IOException if an error occurs
238+ */
239+ long parseDir (String line , String path ) throws IOException {
240+ // read from the end of the line to find the last numeric
241+ // character on the line, then continue until we find the first
242+ // non-numeric character, and everything between that and the last
243+ // numeric character inclusive is our free space bytes count
244+ int bytesStart = 0 ;
245+ int bytesEnd = 0 ;
246+ int j = line .length () - 1 ;
247+ innerLoop1 : while (j >= 0 ) {
248+ char c = line .charAt (j );
249+ if (Character .isDigit (c )) {
250+ // found the last numeric character, this is the end of
251+ // the free space bytes count
252+ bytesEnd = j + 1 ;
253+ break innerLoop1 ;
254+ }
255+ j --;
256+ }
257+ innerLoop2 : while (j >= 0 ) {
258+ char c = line .charAt (j );
259+ if (!Character .isDigit (c ) && c != ',' && c != '.' ) {
260+ // found the next non-numeric character, this is the
261+ // beginning of the free space bytes count
262+ bytesStart = j + 1 ;
263+ break innerLoop2 ;
264+ }
265+ j --;
266+ }
267+ if (j < 0 ) {
268+ throw new IOException (
269+ "Command line 'dir /-c' did not return valid info " +
270+ "for path '" + path + "'" );
271+ }
272+
272273 // remove commas and dots in the bytes count
273274 StringBuffer buf = new StringBuffer (line .substring (bytesStart , bytesEnd ));
274275 for (int k = 0 ; k < buf .length (); k ++) {
275276 if (buf .charAt (k ) == ',' || buf .charAt (k ) == '.' ) {
276277 buf .deleteCharAt (k --);
277278 }
278279 }
279- bytes = Long .parseLong (buf .toString ());
280- return bytes ;
280+ return parseBytes (buf .toString (), path );
281281 }
282282
283+ //-----------------------------------------------------------------------
283284 /**
284285 * Find free space on the *nix platform using the 'df' command.
285286 *
@@ -306,70 +307,126 @@ long freeSpaceUnix(String path, boolean kb, boolean posix) throws IOException {
306307 String [] cmdAttribs =
307308 (flags .length () > 1 ? new String [] {"df" , flags , path } : new String [] {"df" , path });
308309
309- // read the output from the command until we come to the second line
310- long bytes = -1 ;
311- BufferedReader in = null ;
312- try {
313- in = openProcessStream (cmdAttribs );
314- String line1 = in .readLine (); // header line (ignore it)
315- String line2 = in .readLine (); // the line we're interested in
316- String line3 = in .readLine (); // possibly interesting line
317- if (line2 == null ) {
318- // unknown problem, throw exception
310+ // perform the command, asking for up to 3 lines (header, interesting, overflow)
311+ List lines = performCommand (cmdAttribs , 3 );
312+ if (lines .size () < 2 ) {
313+ // unknown problem, throw exception
314+ throw new IOException (
315+ "Command line 'df' did not return info as expected " +
316+ "for path '" + path + "'- response was " + lines );
317+ }
318+ String line2 = (String ) lines .get (1 ); // the line we're interested in
319+
320+ // Now, we tokenize the string. The fourth element is what we want.
321+ StringTokenizer tok = new StringTokenizer (line2 , " " );
322+ if (tok .countTokens () < 4 ) {
323+ // could be long Filesystem, thus data on third line
324+ if (tok .countTokens () == 1 && lines .size () >= 3 ) {
325+ String line3 = (String ) lines .get (2 ); // the line may be interested in
326+ tok = new StringTokenizer (line3 , " " );
327+ } else {
319328 throw new IOException (
320- "Command line 'df' did not return info as expected " +
321- "for path '" + path +
322- "'- response on first line was '" + line1 + "'" );
329+ "Command line 'df' did not return data as expected " +
330+ "for path '" + path + "'- check path is valid" );
323331 }
324- line2 = line2 .trim ();
332+ } else {
333+ tok .nextToken (); // Ignore Filesystem
334+ }
335+ tok .nextToken (); // Ignore 1K-blocks
336+ tok .nextToken (); // Ignore Used
337+ String freeSpace = tok .nextToken ();
338+ return parseBytes (freeSpace , path );
339+ }
325340
326- // Now, we tokenize the string. The fourth element is what we want.
327- StringTokenizer tok = new StringTokenizer (line2 , " " );
328- if (tok .countTokens () < 4 ) {
329- // could be long Filesystem, thus data on third line
330- if (tok .countTokens () == 1 && line3 != null ) {
331- line3 = line3 .trim ();
332- tok = new StringTokenizer (line3 , " " );
333- } else {
334- throw new IOException (
335- "Command line 'df' did not return data as expected " +
336- "for path '" + path + "'- check path is valid" );
337- }
338- } else {
339- tok .nextToken (); // Ignore Filesystem
340- }
341- tok .nextToken (); // Ignore 1K-blocks
342- tok .nextToken (); // Ignore Used
343- String freeSpace = tok .nextToken ();
344- try {
345- bytes = Long .parseLong (freeSpace );
346- } catch (NumberFormatException ex ) {
341+ //-----------------------------------------------------------------------
342+ /**
343+ * Parses the bytes from a string.
344+ *
345+ * @param freeSpace the free space string
346+ * @param path the path
347+ * @return the number of bytes
348+ * @throws IOException if an error occurs
349+ */
350+ long parseBytes (String freeSpace , String path ) throws IOException {
351+ try {
352+ long bytes = Long .parseLong (freeSpace );
353+ if (bytes < 0 ) {
347354 throw new IOException (
348- "Command line 'df' did not return numeric data as expected " +
355+ "Command line 'df' did not find free space in response " +
349356 "for path '" + path + "'- check path is valid" );
350357 }
358+ return bytes ;
359+
360+ } catch (NumberFormatException ex ) {
361+ throw new IOException (
362+ "Command line 'df' did not return numeric data as expected " +
363+ "for path '" + path + "'- check path is valid" );
364+ }
365+ }
351366
367+ /**
368+ * Performs the os command.
369+ *
370+ * @param cmdAttribs the command line parameters
371+ * @return the parsed data
372+ * @throws IOException if an error occurs
373+ */
374+ List performCommand (String [] cmdAttribs , int max ) throws IOException {
375+ List lines = new ArrayList ();
376+ BufferedReader in = null ;
377+ try {
378+ Process proc = openProcess (cmdAttribs );
379+ in = openProcessStream (proc );
380+ String line = in .readLine ();
381+ while (line != null && lines .size () < max ) {
382+ line = line .toLowerCase ().trim ();
383+ lines .add (line );
384+ line = in .readLine ();
385+ }
386+
387+ proc .waitFor ();
388+ if (proc .exitValue () != 0 ) {
389+ // os command problem, throw exception
390+ throw new IOException (
391+ "Command line returned OS error code '" + proc .exitValue () +
392+ "' for command " + Arrays .asList (cmdAttribs ));
393+ }
394+ if (lines .size () == 0 ) {
395+ // unknown problem, throw exception
396+ throw new IOException (
397+ "Command line did not return any info " +
398+ "for command " + Arrays .asList (cmdAttribs ));
399+ }
400+ return lines ;
401+
402+ } catch (InterruptedException ex ) {
403+ throw new IOException (
404+ "Command line threw an InterruptedException '" + ex .getMessage () +
405+ "' for command " + Arrays .asList (cmdAttribs ));
352406 } finally {
353407 IOUtils .closeQuietly (in );
354408 }
409+ }
355410
356- if (bytes < 0 ) {
357- throw new IOException (
358- "Command line 'df' did not find free space in response " +
359- "for path '" + path + "'- check path is valid" );
360- }
361- return bytes ;
411+ /**
412+ * Opens the process to the operating system.
413+ *
414+ * @param cmdAttribs the command line parameters
415+ * @return the process
416+ * @throws IOException if an error occurs
417+ */
418+ Process openProcess (String [] cmdAttribs ) throws IOException {
419+ return Runtime .getRuntime ().exec (cmdAttribs );
362420 }
363421
364422 /**
365- * Opens the stream to be operating system.
423+ * Opens the stream to the operating system.
366424 *
367- * @param params the command parameters
425+ * @param proc the process
368426 * @return a reader
369427 * @throws IOException if an error occurs
370428 */
371- BufferedReader openProcessStream (String [] params ) throws IOException {
372- Process proc = Runtime .getRuntime ().exec (params );
429+ BufferedReader openProcessStream (Process proc ) throws IOException {
373430 return new BufferedReader (
374431 new InputStreamReader (proc .getInputStream ()));
375432 }
0 commit comments