Simulating case sensitivity for win32 files

M

M&M

I understand that windows filesystems are case insensitive though case
preserving. My dilema is that files on my windows server are synced
to a linux web server where case sensitivity does matter.

Is there a way to simulate "-f test.txt" in a case sensitive manner or
perhaps retrieve a file's actual name? I was surprised to learn that
neither glob("test.txt") nor <test.txt> do the job without using
wildcards. I've searched the cpan, faqs, google groups and the
cookbook to no avail.

I've come up with the following but it's very inefficient for
real_filename() to read the directory each time (especially if it is
very large). Sure, I could cache the dir contents in a list or hash,
but then I have the complexity of maintaining it as changes occur. It
works, but how could it be improved? (It doesn't even address
directories...) Certainly others have dealt with this before. Any
suggestions?

Thanks,

Marco Moreno


#!/usr/bin/perl -w

chdir "c:/temp";

my $file = "test.txt";

if (-f $file and not really_exists($file)) {
my $realname = real_filename($file);
warn "Renaming $realname to $file.\n";
rename $realname, $file or die "Can't rename $realname: \n";
}

sub really_exists {
my $filename = shift;
return $filename eq real_filename($filename) ? 1 : 0;
}

sub real_filename {
my $filename = shift;
opendir DIR, "." or die "Can't readdir: $!";
(my $realname) = grep { $filename =~ /^${_}$/i } readdir DIR;
closedir DIR;
return $realname;
}
 
M

Malcolm Dew-Jones

M&M ([email protected]) wrote:
: I understand that windows filesystems are case insensitive though case
: preserving. My dilema is that files on my windows server are synced
: to a linux web server where case sensitivity does matter.

: Is there a way to simulate "-f test.txt" in a case sensitive manner or
: perhaps retrieve a file's actual name? I was surprised to learn that

This sort of information is available in windows, so I would assume that
the various win32 module(s) would have some way to get the information.
 
S

Sisyphus

M&M said:
#!/usr/bin/perl -w

chdir "c:/temp";

my $file = "test.txt";

if (-f $file and not really_exists($file)) {
my $realname = real_filename($file);
warn "Renaming $realname to $file.\n";
rename $realname, $file or die "Can't rename $realname: \n";
}

sub really_exists {
my $filename = shift;
return $filename eq real_filename($filename) ? 1 : 0;
}

sub real_filename {
my $filename = shift;
opendir DIR, "." or die "Can't readdir: $!";
(my $realname) = grep { $filename =~ /^${_}$/i } readdir DIR;
closedir DIR;
return $realname;
}


I think one improvement would be to create an array containing the
actual names of the files so that you aren't constantly re-reading
through the directory.

@real_filenames = grep { -f "$_"} readdir DIR;
# assumes the opened dir is "."
# In general: grep {-f "$dir/$_"} readdir DIR;

really_exists() could then look something like this:

sub really_exists {
my $filename = shift;
for(@real_filenames) {
if($_ eq $filename) {return 1}
if(lc($_) eq lc($filename)) {return 2}
}
return 0;
}

Hth.

Cheers,
Rob
 
R

Roy Johnson

It works, but how could it be improved? (It doesn't even address
directories...) Certainly others have dealt with this before. Any
suggestions?

It can surely be improved. First of all, you call real_filename twice
every time you encounter it. Get rid of the really_exists sub. You
need the real filename, anyway, so having the really_exists sub throw
it away works against you.
if (-f $file) {
my $realname = real_filename($file);
if ($file ne $realname) {
#...renaming...

Now, in real_filename, you've chosen to grep through the entire
directory, with no possibility of short-circuiting. Ouch. Also,
caching is not so difficult to add:
my %file_cache;
sub real_filename {
my $filename = lc(shift); ## Compare as lowercase only
unless (exists $file_cache{$filename}) {
opendir DIR, '.' or die "Can't readdir: $!";
while (my $f = readdir DIR) {
if ($filename eq lc($f)) {
$file_cache{$filename} = $f;
last; ## When you find it, stop reading!
}
}
closedir DIR;
}
return $file_cache{$filename};
}
 
R

Roy Johnson

I just realized that the caching I suggested is pointless, since you
immediately rename the file. Of course, you can update the cache each
time you do a rename.

If you're only going to see each filename once, there's no point in
maintaining a cache. Just slurp the whole directory list into a hash
mapping lc(name)=>name, and then use it to lookup the file names when
you see them.

If you're going to rename all the files that aren't equal to their
canonical names (I'm guessing lowercase?), just do the rename for
every file, and don't worry about those that don't change.

I'm not real sure how you're planning to work with all the files, but
I hope some of these thoughts helped.
 
M

M&M

(e-mail address removed) (Roy Johnson) wrote in message
I appreciate the comments, but this isn't really what I'm after. I'm
looking for a case sensitive "-f" option so I can do something like
this:

if (-f "/Very/Long/Path/To/file.htm")...

Or at least use a function to retrieve the native pathname from the
os:

print realname("c:/very/long/path/to/file.htm");

yeilding

C:/Very/Long/Path/To/file.htm

I searched all the win32 modules on cpan, but I found nothing that
seems to do this. I posted the sample code merely as a proof of
concept that it is theoretically possible though highly inefficient
and impractical - nor does it handle case sensitivity of directories.
Using my method, I would need to read /, /Very, /Very/Long,
/Very/Long/Path.... It quickly becomes obvious that reading each
directory is not the solution. (I hope!)

And caching really isn't an option for me because it assumes that
files will not change. Even if the script handled the complexity of
maintaining the cache as it changed files, it doesn't take into
account multiple threads/processes or file changes by external
processes.

Marco Moreno
 
M

M&M

Ahhh!!! I found Win32::GetLongPathName() that returns the actual
path! I didn't realize this was in the Win32 core module.

Marco
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top