Get free disk space under Windows

M

Marco Schmidt

No, this is not the usual question on how to get free diskspace. The
RFE
<http://developer.java.sun.com/developer/bugParade/bugs/4057701.html>
on that topic is already in its seventh year. :(

This is a pure Java program that parses the output of the 'dir'
command to determine that value. I could use some help with it. The
program works only under Windows, although the same approach could
most likely be used with other operating systems.

I'd like as many people as possible to run this program under their
flavor of Windows and report problems. Java 1.4+ is required.

Two things to point your attention to:

* What other "os.name" strings apart from "Windows NT" and "Windows
2000" exist for the NT flavor of Windows? I guess "Windows XP" and
"Windows 2003", but I'm not sure.

* What other decimal separators are there apart from the comma and the
dot? I'm talking about the characters that are put between groups of
digits in the dir number output. Example: the comma in 1,234 (USA) or
the dot in 1.234 (Germany). It's probably helpful that this is an
international newsgroup because many language versions of Windows are
installed on readers' systems. If that character is not removed, the
output cannot be parsed. The character is probably the one returned by
java/text/DecimalFormatSymbols.html#getGroupingSeparator().

Here's the program:
=== filesystem.Diskspace
package filesystem;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
* Determine free disk space for a given directory by
* parsing the output of the dir command.
* This class is inspired by the code at
*
http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_20088852.html
* Works only under Windows under certain circumstances.
* Yes, it's that shaky.
* Requires Java 1.4 or higher.
* @author Marco Schmidt
*/
public class Diskspace
{
private Diskspace()
{
// prevent instantiation of this class
}

/**
* Return available free disk space for a directory.
* @param dirName name of the directory
* @return free disk space in bytes or -1 if unknown
*/
public static long getFreeDiskSpace(String dirName)
{
try
{
// guess correct 'dir' command by looking at the
// operating system name
String os = System.getProperty("os.name");
String command;
if (os.equals("Windows NT") ||
os.equals("Windows 2000"))
{
command = "cmd.exe /c dir " + dirName;
}
else
{
command = "command.com /c dir " + dirName;
}
// run the dir command on the argument directory name
Runtime runtime = Runtime.getRuntime();
Process process = null;
process = runtime.exec(command);
if (process == null)
{
return -1;
}
// read the output of the dir command
// only the last line is of interest
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
String freeSpace = null;
while ((line = in.readLine()) != null)
{
freeSpace = line;
}
if (freeSpace == null)
{
return -1;
}
process.destroy();
// remove dots & commas & leading and trailing whitespace
freeSpace = freeSpace.trim();
freeSpace = freeSpace.replaceAll("\\.", "");
freeSpace = freeSpace.replaceAll(",", "");
String[] items = freeSpace.split(" ");
// the first valid numeric value in items after(!) index 0
// is probably the free disk space
int index = 1;
while (index < items.length)
{
try
{
long bytes = Long.parseLong(items[index++]);
return bytes;
}
catch (NumberFormatException nfe)
{
}
}
return -1;
}
catch (Exception exception)
{
return -1;
}
}

/**
* Command line program to print the free diskspace to stdout
* for all 26 potential root directories A:\ to Z:\
* (when no parameters are given to this program)
* or for those directories (drives) specified as parameters.
* @param args program parameters
*/
public static void main(String[] args)
{
if (args.length == 0)
{
for (char c = 'A'; c <= 'Z'; c++)
{
String dirName = c + ":\\";
System.out.println(dirName + " " +
getFreeDiskSpace(dirName));
}
}
else
{
for (int i = 0; i < args.length; i++)
{
System.out.println(args + " " +
getFreeDiskSpace(args));
}
}
}
}
===

Regards,
Marco
 
A

Andrew Thompson

| No, this is not the usual question on how to get free
diskspace.

I got a dialog..
"C:\WINDOWS\system32\ntvdm.exe
Error while setting up environment for the application. ..."

(on the command line) *
d -1

Also tried with upper case 'D'..

This is on XP.
I am not sure what other details I can provide..
(....err. I am using 1.4.2. And, yes I do have a
'D' drive..)

[ * It was quite a bother dealing with the 26
dialogs when I ran it w/out args! ]
 
M

Marco Schmidt

Andrew Thompson:
I got a dialog..
"C:\WINDOWS\system32\ntvdm.exe
Error while setting up environment for the application. ..."

(on the command line) *
d -1

Also tried with upper case 'D'..

The arguments must be valid directories, D:\ would probably be
appropriate in your case.
This is on XP.
I am not sure what other details I can provide..

What does System.getProperty("os.name"); return for XP?
(....err. I am using 1.4.2. And, yes I do have a
'D' drive..)

[ * It was quite a bother dealing with the 26
dialogs when I ran it w/out args! ]

Sorry for that. It looks like the class is an even dirtier hack than I
thought. I've experienced dialogs when trying to access invalid drives
from within a Java program under Windows, but that was fixed (for me)
in some 1.3.x version. The Diskspace class requires 1.4+, so I thought
that wouldn't be a problem.

When I run the program without any arguments, I don't have the problem
(Win2000, JDK 1.4.2), I quickly get the list from A:\ to Z:\, with
only a small pause at the beginning when the floppy disk drive A:\ is
accessed.

Regards,
Marco
 
N

nos

Marco Schmidt said:
Andrew Thompson:


The arguments must be valid directories, D:\ would probably be
appropriate in your case.


What does System.getProperty("os.name"); return for XP?

it returns "Windows XP"
(....err. I am using 1.4.2. And, yes I do have a
'D' drive..)

[ * It was quite a bother dealing with the 26
dialogs when I ran it w/out args! ]

Sorry for that. It looks like the class is an even dirtier hack than I
thought. I've experienced dialogs when trying to access invalid drives
from within a Java program under Windows, but that was fixed (for me)
in some 1.3.x version. The Diskspace class requires 1.4+, so I thought
that wouldn't be a problem.

When I run the program without any arguments, I don't have the problem
(Win2000, JDK 1.4.2), I quickly get the list from A:\ to Z:\, with
only a small pause at the beginning when the floppy disk drive A:\ is
accessed.

Regards,
Marco
 
A

Andrew Thompson

| | > Andrew Thompson:
...
| > What does System.getProperty("os.name"); return for XP?
|
| it returns "Windows XP"

Added that line to the listing around line 40, vis...

if (os.equals("Windows NT") ||
os.equals("Windows 2000") ||
os.equals("Windows XP"))
{
command = "cmd.exe /c dir " + dirName;....

Once I had confirmed the dialog was gone,
I removed the argument and ran for all drives.
Works fine...
The figures reported for C and D agreed with
Windows values, and it reported there was '0'
on F (a CD).

HTH
 
M

Marco Schmidt

Andrew Thompson:

[...]
Once I had confirmed the dialog was gone,
I removed the argument and ran for all drives.
Works fine...
The figures reported for C and D agreed with
Windows values, and it reported there was '0'
on F (a CD).

HTH

Thanks, helps indeed. However, in order to keep the class working with
newer NT variants I've changed the logic for picking the command
statement:

if (os.equals("Windows 95") ||
os.equals("Windows 98"))
{
command = "command.com /c dir " + dirName;
}
else
{
command = "cmd.exe /c dir " + dirName;
}

That way, Windows 2003 and anything after that will hopefully work.

I also found "Windows CE" at
<http://www.tolstoy.com/samizdat/sysprops.html#win>, but I doubt that
there will be a 1.4 JRE/JDK anytime soon for CE. Besides, I don't know
if it has either command.com or cmd.exe.

Regards,
Marco
 
J

Jon A. Cruz

Andrew said:
Added that line to the listing around line 40, vis...

if (os.equals("Windows NT") ||
os.equals("Windows 2000") ||
os.equals("Windows XP"))
{

That's actually pretty fragile code.

First it counts on those strings and exactly those strings being there.
If the vendor has a different naming, or adds an extension char, etc.
then you'll miss.

At the least, I'd make it do a case insensitive check. And might even do
a substring search, especialy if your intent is to get all versions of
windows and not to exclude Windows 96/98/NT.


this might be much safer:
if ( os.toLowerCase().indexOf("windows ") >= 0 )


However, OS checks of that type are fairly fragile in general, and even
Microsoft has been advising against doing that for years. Generally some
kid of feature check is better. Perhaps checking for the presence of
cmd.exe or command.com (test run with Runtime.exec) and then setting a
static varible to indicate which you have.
 
M

Marco Schmidt

Jon A. Cruz:
That's actually pretty fragile code.

Very true, but I've called the whole thing a dirty hack myself, and I
meant it. However...

[...]
this might be much safer:
if ( os.toLowerCase().indexOf("windows ") >= 0 )

....the very point of the code is to differentiate between Windows
NT/2000/XP (the "NT line") and Windows 95/98/? (the other Windows
line) in order to pick the correct way of calling the shell. They all
have "windows " in their os.name system property.
However, OS checks of that type are fairly fragile in general, and even
Microsoft has been advising against doing that for years. Generally some
kid of feature check is better. Perhaps checking for the presence of
cmd.exe or command.com (test run with Runtime.exec) and then setting a
static varible to indicate which you have.

That's a good idea. But I'm not so certain where to search. Sometimes
it's c:\windows, sometimes c:\winnt, and I'm sure that c:\ is not
mandatory as the system drive.

Regards,
Marco
 
J

Joseph Dionne

Marco said:
Jon A. Cruz:

That's actually pretty fragile code.


Very true, but I've called the whole thing a dirty hack myself, and I
meant it. However...

[...]
this might be much safer:
if ( os.toLowerCase().indexOf("windows ") >= 0 )


...the very point of the code is to differentiate between Windows
NT/2000/XP (the "NT line") and Windows 95/98/? (the other Windows
line) in order to pick the correct way of calling the shell. They all
have "windows " in their os.name system property.

However, OS checks of that type are fairly fragile in general, and even
Microsoft has been advising against doing that for years. Generally some
kid of feature check is better. Perhaps checking for the presence of
cmd.exe or command.com (test run with Runtime.exec) and then setting a
static varible to indicate which you have.


That's a good idea. But I'm not so certain where to search. Sometimes
it's c:\windows, sometimes c:\winnt, and I'm sure that c:\ is not
mandatory as the system drive.

Regards,
Marco

If you are using a VM that supports System.getenv(), you can look for
SYSTEMROOT. If you cannot control the VM version, you can run "ver
 
J

Jon A. Cruz

Marco said:
...the very point of the code is to differentiate between Windows
NT/2000/XP (the "NT line") and Windows 95/98/?

You shouldn't. :)

Whenever possible, "os.name" strings should be treated like arbitrary
terms set by a marketing department and only suitable for displaying to
the user in some 'about' box.

:)


Now, for practical reasons, it might be ok to use it as an initial
starting point guess when you begin platform feature checks.

NT/2000/XP (the "NT line") and Windows 95/98/? (the other Windows
line) in order to pick the correct way of calling the shell.

OK. Now *that's* what you should be caring about.

They all
have "windows " in their os.name system property.

.... at the moment, on the systems you've been able to test, on the Java
VM's you've been able to test, etc.


That's a good idea. But I'm not so certain where to search. Sometimes
it's c:\windows, sometimes c:\winnt, and I'm sure that c:\ is not
mandatory as the system drive.


Don't search for the presence of a file.

Instead, *check* for the availability to run that program.

That is, just try to Runtime.exec() command.com and cmd.exe. In a
DOS/Windows world, those should be on the path so that you shouldn't
have to explicitly specify their absolute path.

I've used the approach in the past a few times with browser launching a
URL. Detecting Mac OS is much easier, as you can use reflection to check
for some MRJ stuff.

Oh, and as far as Windows paths and names go, you're right. Don't count
on anything. I always had my Windows 9x installs in strange named
directories, and non-english Windows installs often are in the local
language. Oh, and any Japanese PC's that follow the 'PC98' (not 1998,
btw) conventions start with A: as the first hard drive, etc. :)
 
J

Jon A. Cruz

Marco said:
* What other decimal separators are there apart from the comma and the
dot? I'm talking about the characters that are put between groups of
digits in the dir number output. Example: the comma in 1,234 (USA) or
the dot in 1.234 (Germany). It's probably helpful that this is an
international newsgroup because many language versions of Windows are
installed on readers' systems. If that character is not removed, the
output cannot be parsed. The character is probably the one returned by
java/text/DecimalFormatSymbols.html#getGroupingSeparator().
[SNIP]

// remove dots & commas & leading and trailing whitespace
freeSpace = freeSpace.trim();
freeSpace = freeSpace.replaceAll("\\.", "");
freeSpace = freeSpace.replaceAll(",", "");
String[] items = freeSpace.split(" ");
// the first valid numeric value in items after(!) index 0
// is probably the free disk space
int index = 1;
while (index < items.length)
{
try
{
long bytes = Long.parseLong(items[index++]);
return bytes;
}
catch (NumberFormatException nfe)
{
}
}
return -1;
}

Look into using the parse() method of MessageFormat and it's friends
NumberFormat and DecimalFormat instead. They should be interpreting
numbers in the current locale's default. Plus you can use that to split
the string at the same time by parsing with MessageFormat (though if
there's non-numeric input, your parse pattern string would have to match).

Anyway, those are all locale-aware, while Long is not. So those should
be what will help you better.
 
N

nos

Jon A. Cruz said:
You shouldn't. :)

Whenever possible, "os.name" strings should be treated like arbitrary
terms set by a marketing department and only suitable for displaying to
the user in some 'about' box.

:)


Now, for practical reasons, it might be ok to use it as an initial
starting point guess when you begin platform feature checks.



OK. Now *that's* what you should be caring about.



... at the moment, on the systems you've been able to test, on the Java
VM's you've been able to test, etc.





Don't search for the presence of a file.

Instead, *check* for the availability to run that program.

That is, just try to Runtime.exec() command.com and cmd.exe. In a
DOS/Windows world, those should be on the path so that you shouldn't
have to explicitly specify their absolute path.

I've used the approach in the past a few times with browser launching a
URL. Detecting Mac OS is much easier, as you can use reflection to check
for some MRJ stuff.

Oh, and as far as Windows paths and names go, you're right. Don't count
on anything. I always had my Windows 9x installs in strange named
directories, and non-english Windows installs often are in the local
language. Oh, and any Japanese PC's that follow the 'PC98' (not 1998,
btw) conventions start with A: as the first hard drive, etc. :)

Here's what I would do, it seems like the Java way to do things.
(i.e. machine independent)
Use JNI to invoke a native method that calls the standard windows
routine to determine disk memory, _getdiskfree(disk_drive, &df);
Which I believe will work on all windows versions. Then invoke the
JNI native method in a try block. Then if it doesn't work (maybe
you are on Linux) handle the exception.
 
J

Jon A. Cruz

nos said:
Here's what I would do, it seems like the Java way to do things.
(i.e. machine independent)

Actually, that's *very* machine dependent in that you have to write JNI
code specific to each and every platform you'll run on, and have to get
it installed.

For Windows, you'd need one or two DLLs. For each Unix varient you'd
need a custom .so, etc.
 
N

nos

Jon A. Cruz said:
Actually, that's *very* machine dependent in that you have to write JNI
code specific to each and every platform you'll run on, and have to get
it installed.

For Windows, you'd need one or two DLLs. For each Unix varient you'd
need a custom .so, etc.
Well the OP said
"..... program works only under Windows, ......"
so I figured he only needed it to work under Windows.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top